diff options
Diffstat (limited to 'security')
27 files changed, 1425 insertions, 447 deletions
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 240a53387e6b..f72406fe1bf2 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1252,7 +1252,8 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_free, apparmor_task_free), LSM_HOOK_INIT(task_alloc, apparmor_task_alloc), - LSM_HOOK_INIT(task_getsecid, apparmor_task_getsecid), + LSM_HOOK_INIT(task_getsecid_subj, apparmor_task_getsecid), + LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid), LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), LSM_HOOK_INIT(task_kill, apparmor_task_kill), diff --git a/security/commoncap.c b/security/commoncap.c index 1c519c875217..3f810d37b71b 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -50,7 +50,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) /** * cap_capable - Determine whether a task has a particular effective capability * @cred: The credentials to use - * @ns: The user namespace in which we need the capability + * @targ_ns: The user namespace in which we need the capability * @cap: The capability to check for * @opts: Bitmask of options defined in include/linux/security.h * @@ -289,7 +289,7 @@ int cap_capset(struct cred *new, * affects the security markings on that inode, and if it is, should * inode_killpriv() be invoked or the change rejected. * - * Returns 1 if security.capability has a value, meaning inode_killpriv() + * Return: 1 if security.capability has a value, meaning inode_killpriv() * is required, 0 otherwise, meaning inode_killpriv() is not required. */ int cap_inode_need_killpriv(struct dentry *dentry) @@ -315,7 +315,7 @@ int cap_inode_need_killpriv(struct dentry *dentry) * permissions. On non-idmapped mounts or if permission checking is to be * performed on the raw inode simply passs init_user_ns. * - * Returns 0 if successful, -ve on error. + * Return: 0 if successful, -ve on error. */ int cap_inode_killpriv(struct user_namespace *mnt_userns, struct dentry *dentry) { @@ -400,7 +400,7 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, &tmpbuf, size, GFP_NOFS); dput(dentry); - if (ret < 0) + if (ret < 0 || !tmpbuf) return ret; fs_ns = inode->i_sb->s_user_ns; @@ -532,7 +532,7 @@ static bool validheader(size_t size, const struct vfs_cap_data *cap) * permissions. On non-idmapped mounts or if permission checking is to be * performed on the raw inode simply passs init_user_ns. * - * If all is ok, we return the new size, on error return < 0. + * Return: On success, return the new size; on error, return < 0. */ int cap_convert_nscap(struct user_namespace *mnt_userns, struct dentry *dentry, const void **ivalue, size_t size) @@ -881,7 +881,9 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old, * * Set up the proposed credentials for a new execution context being * constructed by execve(). The proposed creds in @bprm->cred is altered, - * which won't take effect immediately. Returns 0 if successful, -ve on error. + * which won't take effect immediately. + * + * Return: 0 if successful, -ve on error. */ int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file) { @@ -1117,7 +1119,9 @@ static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old) * @flags: Indications of what has changed * * Fix up the results of setuid() call before the credential changes are - * actually applied, returning 0 to grant the changes, -ve to deny them. + * actually applied. + * + * Return: 0 to grant the changes, -ve to deny them. */ int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags) { @@ -1187,7 +1191,9 @@ static int cap_safe_nice(struct task_struct *p) * @p: The task to affect * * Detemine if the requested scheduler policy change is permitted for the - * specified task, returning 0 if permission is granted, -ve if denied. + * specified task. + * + * Return: 0 if permission is granted, -ve if denied. */ int cap_task_setscheduler(struct task_struct *p) { @@ -1195,12 +1201,14 @@ int cap_task_setscheduler(struct task_struct *p) } /** - * cap_task_ioprio - Detemine if I/O priority change is permitted + * cap_task_setioprio - Detemine if I/O priority change is permitted * @p: The task to affect * @ioprio: The I/O priority to set * * Detemine if the requested I/O priority change is permitted for the specified - * task, returning 0 if permission is granted, -ve if denied. + * task. + * + * Return: 0 if permission is granted, -ve if denied. */ int cap_task_setioprio(struct task_struct *p, int ioprio) { @@ -1208,12 +1216,14 @@ int cap_task_setioprio(struct task_struct *p, int ioprio) } /** - * cap_task_ioprio - Detemine if task priority change is permitted + * cap_task_setnice - Detemine if task priority change is permitted * @p: The task to affect * @nice: The nice value to set * * Detemine if the requested task priority change is permitted for the - * specified task, returning 0 if permission is granted, -ve if denied. + * specified task. + * + * Return: 0 if permission is granted, -ve if denied. */ int cap_task_setnice(struct task_struct *p, int nice) { @@ -1243,12 +1253,15 @@ static int cap_prctl_drop(unsigned long cap) /** * cap_task_prctl - Implement process control functions for this security module * @option: The process control function requested - * @arg2, @arg3, @arg4, @arg5: The argument data for this function + * @arg2: The argument data for this function + * @arg3: The argument data for this function + * @arg4: The argument data for this function + * @arg5: The argument data for this function * * Allow process control functions (sys_prctl()) to alter capabilities; may * also deny access to other functions not otherwise implemented here. * - * Returns 0 or +ve on success, -ENOSYS if this function is not implemented + * Return: 0 or +ve on success, -ENOSYS if this function is not implemented * here, other -ve on error. If -ENOSYS is returned, sys_prctl() and other LSM * modules will consider performing the function. */ @@ -1383,7 +1396,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, * @pages: The size of the mapping * * Determine whether the allocation of a new virtual mapping by the current - * task is permitted, returning 1 if permission is granted, 0 if not. + * task is permitted. + * + * Return: 1 if permission is granted, 0 if not. */ int cap_vm_enough_memory(struct mm_struct *mm, long pages) { @@ -1396,14 +1411,15 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) return cap_sys_admin; } -/* +/** * cap_mmap_addr - check if able to map given addr * @addr: address attempting to be mapped * * If the process is attempting to map memory below dac_mmap_min_addr they need * CAP_SYS_RAWIO. The other parameters to this function are unused by the - * capability security module. Returns 0 if this mapping should be allowed - * -EPERM if not. + * capability security module. + * + * Return: 0 if this mapping should be allowed or -EPERM if not. */ int cap_mmap_addr(unsigned long addr) { diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index a662024b4c70..23240d793b07 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -84,6 +84,7 @@ int asymmetric_verify(struct key *keyring, const char *sig, { struct public_key_signature pks; struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; + const struct public_key *pk; struct key *key; int ret; @@ -105,23 +106,20 @@ int asymmetric_verify(struct key *keyring, const char *sig, memset(&pks, 0, sizeof(pks)); pks.hash_algo = hash_algo_name[hdr->hash_algo]; - switch (hdr->hash_algo) { - case HASH_ALGO_STREEBOG_256: - case HASH_ALGO_STREEBOG_512: - /* EC-RDSA and Streebog should go together. */ - pks.pkey_algo = "ecrdsa"; - pks.encoding = "raw"; - break; - case HASH_ALGO_SM3_256: - /* SM2 and SM3 should go together. */ - pks.pkey_algo = "sm2"; - pks.encoding = "raw"; - break; - default: - pks.pkey_algo = "rsa"; + + pk = asymmetric_key_public_key(key); + pks.pkey_algo = pk->pkey_algo; + if (!strcmp(pk->pkey_algo, "rsa")) pks.encoding = "pkcs1"; - break; - } + else if (!strncmp(pk->pkey_algo, "ecdsa-", 6)) + /* edcsa-nist-p192 etc. */ + pks.encoding = "x962"; + else if (!strcmp(pk->pkey_algo, "ecrdsa") || + !strcmp(pk->pkey_algo, "sm2")) + pks.encoding = "raw"; + else + return -ENOPKG; + pks.digest = (u8 *)data; pks.digest_size = datalen; pks.s = hdr->sig; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 8e8b5251550e..f0e448ed1f9f 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -24,10 +24,6 @@ #include "../integrity.h" -#ifdef CONFIG_HAVE_IMA_KEXEC -#include <asm/ima.h> -#endif - enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN, IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII }; enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 }; diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 565e33ff19d0..4e5eb0236278 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -76,7 +76,7 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode, if (!ima_appraise) return 0; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return ima_match_policy(mnt_userns, inode, current_cred(), secid, func, mask, IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL); } diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index e29bea3dd4cc..667887665823 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -10,6 +10,7 @@ #include <linux/seq_file.h> #include <linux/vmalloc.h> #include <linux/kexec.h> +#include <linux/of.h> #include "ima.h" #ifdef CONFIG_IMA_KEXEC @@ -123,12 +124,8 @@ void ima_add_kexec_buffer(struct kimage *image) return; } - ret = arch_ima_add_kexec_buffer(image, kbuf.mem, kexec_segment_size); - if (ret) { - pr_err("Error passing over kexec measurement buffer.\n"); - return; - } - + image->ima_buffer_addr = kbuf.mem; + image->ima_buffer_size = kexec_segment_size; image->ima_buffer = kexec_buffer; pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n", diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 9ef748ea829f..b85d9e429426 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -391,7 +391,7 @@ int ima_file_mmap(struct file *file, unsigned long prot) u32 secid; if (file && (prot & PROT_EXEC)) { - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return process_measurement(file, current_cred(), secid, NULL, 0, MAY_EXEC, MMAP_CHECK); } @@ -429,7 +429,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC)) return 0; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); inode = file_inode(vma->vm_file); action = ima_get_action(file_mnt_user_ns(vma->vm_file), inode, current_cred(), secid, MAY_EXEC, MMAP_CHECK, @@ -470,7 +470,7 @@ int ima_bprm_check(struct linux_binprm *bprm) int ret; u32 secid; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0, MAY_EXEC, BPRM_CHECK); if (ret) @@ -495,7 +495,7 @@ int ima_file_check(struct file *file, int mask) { u32 secid; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return process_measurement(file, current_cred(), secid, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND), FILE_CHECK); @@ -686,7 +686,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id, /* Read entire file for all partial reads. */ func = read_idmap[read_id] ?: FILE_CHECK; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return process_measurement(file, current_cred(), secid, NULL, 0, MAY_READ, func); } @@ -729,7 +729,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size, } func = read_idmap[read_id] ?: FILE_CHECK; - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); return process_measurement(file, current_cred(), secid, buf, size, MAY_READ, func); } @@ -872,7 +872,7 @@ void process_buffer_measurement(struct user_namespace *mnt_userns, * buffer measurements. */ if (func) { - security_task_getsecid(current, &secid); + security_task_getsecid_subj(current, &secid); action = ima_get_action(mnt_userns, inode, current_cred(), secid, 0, func, &pcr, &template, func_data); diff --git a/security/integrity/platform_certs/keyring_handler.c b/security/integrity/platform_certs/keyring_handler.c index c5ba695c10e3..5604bd57c990 100644 --- a/security/integrity/platform_certs/keyring_handler.c +++ b/security/integrity/platform_certs/keyring_handler.c @@ -56,6 +56,15 @@ static __init void uefi_blacklist_binary(const char *source, } /* + * Add an X509 cert to the revocation list. + */ +static __init void uefi_revocation_list_x509(const char *source, + const void *data, size_t len) +{ + add_key_to_revocation_list(data, len); +} + +/* * Return the appropriate handler for particular signature list types found in * the UEFI db and MokListRT tables. */ @@ -76,5 +85,7 @@ __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type) return uefi_blacklist_x509_tbs; if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0) return uefi_blacklist_binary; + if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) + return uefi_revocation_list_x509; return 0; } diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c index ee4b4c666854..f290f78c3f30 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -132,8 +132,9 @@ static int __init load_moklist_certs(void) static int __init load_uefi_certs(void) { efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; - void *db = NULL, *dbx = NULL; - unsigned long dbsize = 0, dbxsize = 0; + efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; + void *db = NULL, *dbx = NULL, *mokx = NULL; + unsigned long dbsize = 0, dbxsize = 0, mokxsize = 0; efi_status_t status; int rc = 0; @@ -175,6 +176,21 @@ static int __init load_uefi_certs(void) kfree(dbx); } + mokx = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &status); + if (!mokx) { + if (status == EFI_NOT_FOUND) + pr_debug("mokx variable wasn't found\n"); + else + pr_info("Couldn't get mokx list\n"); + } else { + rc = parse_efi_signature_list("UEFI:MokListXRT", + mokx, mokxsize, + get_handler_for_dbx); + if (rc) + pr_err("Couldn't parse mokx signatures %d\n", rc); + kfree(mokx); + } + /* Load the MokListRT certs */ rc = load_moklist_certs(); diff --git a/security/keys/Kconfig b/security/keys/Kconfig index c161642a8484..64b81abd087e 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -75,6 +75,9 @@ config TRUSTED_KEYS select CRYPTO_HMAC select CRYPTO_SHA1 select CRYPTO_HASH_INFO + select ASN1_ENCODER + select OID_REGISTRY + select ASN1 help This option provides support for creating, sealing, and unsealing keys in the kernel. Trusted keys are random number symmetric keys, diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile index 7b73cebbb378..feb8b6c3cc79 100644 --- a/security/keys/trusted-keys/Makefile +++ b/security/keys/trusted-keys/Makefile @@ -4,5 +4,11 @@ # obj-$(CONFIG_TRUSTED_KEYS) += trusted.o +trusted-y += trusted_core.o trusted-y += trusted_tpm1.o + +$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h trusted-y += trusted_tpm2.o +trusted-y += tpm2key.asn1.o + +trusted-$(CONFIG_TEE) += trusted_tee.o diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1 new file mode 100644 index 000000000000..f57f869ad600 --- /dev/null +++ b/security/keys/trusted-keys/tpm2key.asn1 @@ -0,0 +1,11 @@ +--- +--- ASN.1 for TPM 2.0 keys +--- + +TPMKey ::= SEQUENCE { + type OBJECT IDENTIFIER ({tpm2_key_type}), + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + parent INTEGER ({tpm2_key_parent}), + pubkey OCTET STRING ({tpm2_key_pub}), + privkey OCTET STRING ({tpm2_key_priv}) + } diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c new file mode 100644 index 000000000000..d5c891d8d353 --- /dev/null +++ b/security/keys/trusted-keys/trusted_core.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 IBM Corporation + * Copyright (c) 2019-2021, Linaro Limited + * + * See Documentation/security/keys/trusted-encrypted.rst + */ + +#include <keys/user-type.h> +#include <keys/trusted-type.h> +#include <keys/trusted_tee.h> +#include <keys/trusted_tpm.h> +#include <linux/capability.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/key-type.h> +#include <linux/module.h> +#include <linux/parser.h> +#include <linux/rcupdate.h> +#include <linux/slab.h> +#include <linux/static_call.h> +#include <linux/string.h> +#include <linux/uaccess.h> + +static char *trusted_key_source; +module_param_named(source, trusted_key_source, charp, 0); +MODULE_PARM_DESC(source, "Select trusted keys source (tpm or tee)"); + +static const struct trusted_key_source trusted_key_sources[] = { +#if defined(CONFIG_TCG_TPM) + { "tpm", &trusted_key_tpm_ops }, +#endif +#if defined(CONFIG_TEE) + { "tee", &trusted_key_tee_ops }, +#endif +}; + +DEFINE_STATIC_CALL_NULL(trusted_key_init, *trusted_key_sources[0].ops->init); +DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal); +DEFINE_STATIC_CALL_NULL(trusted_key_unseal, + *trusted_key_sources[0].ops->unseal); +DEFINE_STATIC_CALL_NULL(trusted_key_get_random, + *trusted_key_sources[0].ops->get_random); +DEFINE_STATIC_CALL_NULL(trusted_key_exit, *trusted_key_sources[0].ops->exit); +static unsigned char migratable; + +enum { + Opt_err, + Opt_new, Opt_load, Opt_update, +}; + +static const match_table_t key_tokens = { + {Opt_new, "new"}, + {Opt_load, "load"}, + {Opt_update, "update"}, + {Opt_err, NULL} +}; + +/* + * datablob_parse - parse the keyctl data and fill in the + * payload structure + * + * On success returns 0, otherwise -EINVAL. + */ +static int datablob_parse(char **datablob, struct trusted_key_payload *p) +{ + substring_t args[MAX_OPT_ARGS]; + long keylen; + int ret = -EINVAL; + int key_cmd; + char *c; + + /* main command */ + c = strsep(datablob, " \t"); + if (!c) + return -EINVAL; + key_cmd = match_token(c, key_tokens, args); + switch (key_cmd) { + case Opt_new: + /* first argument is key size */ + c = strsep(datablob, " \t"); + if (!c) + return -EINVAL; + ret = kstrtol(c, 10, &keylen); + if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE) + return -EINVAL; + p->key_len = keylen; + ret = Opt_new; + break; + case Opt_load: + /* first argument is sealed blob */ + c = strsep(datablob, " \t"); + if (!c) + return -EINVAL; + p->blob_len = strlen(c) / 2; + if (p->blob_len > MAX_BLOB_SIZE) + return -EINVAL; + ret = hex2bin(p->blob, c, p->blob_len); + if (ret < 0) + return -EINVAL; + ret = Opt_load; + break; + case Opt_update: + ret = Opt_update; + break; + case Opt_err: + return -EINVAL; + } + return ret; +} + +static struct trusted_key_payload *trusted_payload_alloc(struct key *key) +{ + struct trusted_key_payload *p = NULL; + int ret; + + ret = key_payload_reserve(key, sizeof(*p)); + if (ret < 0) + goto err; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + goto err; + + p->migratable = migratable; +err: + return p; +} + +/* + * trusted_instantiate - create a new trusted key + * + * Unseal an existing trusted blob or, for a new key, get a + * random key, then seal and create a trusted key-type key, + * adding it to the specified keyring. + * + * On success, return 0. Otherwise return errno. + */ +static int trusted_instantiate(struct key *key, + struct key_preparsed_payload *prep) +{ + struct trusted_key_payload *payload = NULL; + size_t datalen = prep->datalen; + char *datablob, *orig_datablob; + int ret = 0; + int key_cmd; + size_t key_len; + + if (datalen <= 0 || datalen > 32767 || !prep->data) + return -EINVAL; + + orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL); + if (!datablob) + return -ENOMEM; + memcpy(datablob, prep->data, datalen); + datablob[datalen] = '\0'; + + payload = trusted_payload_alloc(key); + if (!payload) { + ret = -ENOMEM; + goto out; + } + + key_cmd = datablob_parse(&datablob, payload); + if (key_cmd < 0) { + ret = key_cmd; + goto out; + } + + dump_payload(payload); + + switch (key_cmd) { + case Opt_load: + ret = static_call(trusted_key_unseal)(payload, datablob); + dump_payload(payload); + if (ret < 0) + pr_info("key_unseal failed (%d)\n", ret); + break; + case Opt_new: + key_len = payload->key_len; + ret = static_call(trusted_key_get_random)(payload->key, + key_len); + if (ret < 0) + goto out; + + if (ret != key_len) { + pr_info("key_create failed (%d)\n", ret); + ret = -EIO; + goto out; + } + + ret = static_call(trusted_key_seal)(payload, datablob); + if (ret < 0) + pr_info("key_seal failed (%d)\n", ret); + break; + default: + ret = -EINVAL; + } +out: + kfree_sensitive(orig_datablob); + if (!ret) + rcu_assign_keypointer(key, payload); + else + kfree_sensitive(payload); + return ret; +} + +static void trusted_rcu_free(struct rcu_head *rcu) +{ + struct trusted_key_payload *p; + + p = container_of(rcu, struct trusted_key_payload, rcu); + kfree_sensitive(p); +} + +/* + * trusted_update - reseal an existing key with new PCR values + */ +static int trusted_update(struct key *key, struct key_preparsed_payload *prep) +{ + struct trusted_key_payload *p; + struct trusted_key_payload *new_p; + size_t datalen = prep->datalen; + char *datablob, *orig_datablob; + int ret = 0; + + if (key_is_negative(key)) + return -ENOKEY; + p = key->payload.data[0]; + if (!p->migratable) + return -EPERM; + if (datalen <= 0 || datalen > 32767 || !prep->data) + return -EINVAL; + + orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL); + if (!datablob) + return -ENOMEM; + + new_p = trusted_payload_alloc(key); + if (!new_p) { + ret = -ENOMEM; + goto out; + } + + memcpy(datablob, prep->data, datalen); + datablob[datalen] = '\0'; + ret = datablob_parse(&datablob, new_p); + if (ret != Opt_update) { + ret = -EINVAL; + kfree_sensitive(new_p); + goto out; + } + + /* copy old key values, and reseal with new pcrs */ + new_p->migratable = p->migratable; + new_p->key_len = p->key_len; + memcpy(new_p->key, p->key, p->key_len); + dump_payload(p); + dump_payload(new_p); + + ret = static_call(trusted_key_seal)(new_p, datablob); + if (ret < 0) { + pr_info("key_seal failed (%d)\n", ret); + kfree_sensitive(new_p); + goto out; + } + + rcu_assign_keypointer(key, new_p); + call_rcu(&p->rcu, trusted_rcu_free); +out: + kfree_sensitive(orig_datablob); + return ret; +} + +/* + * trusted_read - copy the sealed blob data to userspace in hex. + * On success, return to userspace the trusted key datablob size. + */ +static long trusted_read(const struct key *key, char *buffer, + size_t buflen) +{ + const struct trusted_key_payload *p; + char *bufp; + int i; + + p = dereference_key_locked(key); + if (!p) + return -EINVAL; + + if (buffer && buflen >= 2 * p->blob_len) { + bufp = buffer; + for (i = 0; i < p->blob_len; i++) + bufp = hex_byte_pack(bufp, p->blob[i]); + } + return 2 * p->blob_len; +} + +/* + * trusted_destroy - clear and free the key's payload + */ +static void trusted_destroy(struct key *key) +{ + kfree_sensitive(key->payload.data[0]); +} + +struct key_type key_type_trusted = { + .name = "trusted", + .instantiate = trusted_instantiate, + .update = trusted_update, + .destroy = trusted_destroy, + .describe = user_describe, + .read = trusted_read, +}; +EXPORT_SYMBOL_GPL(key_type_trusted); + +static int __init init_trusted(void) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(trusted_key_sources); i++) { + if (trusted_key_source && + strncmp(trusted_key_source, trusted_key_sources[i].name, + strlen(trusted_key_sources[i].name))) + continue; + + static_call_update(trusted_key_init, + trusted_key_sources[i].ops->init); + static_call_update(trusted_key_seal, + trusted_key_sources[i].ops->seal); + static_call_update(trusted_key_unseal, + trusted_key_sources[i].ops->unseal); + static_call_update(trusted_key_get_random, + trusted_key_sources[i].ops->get_random); + static_call_update(trusted_key_exit, + trusted_key_sources[i].ops->exit); + migratable = trusted_key_sources[i].ops->migratable; + + ret = static_call(trusted_key_init)(); + if (!ret) + break; + } + + /* + * encrypted_keys.ko depends on successful load of this module even if + * trusted key implementation is not found. + */ + if (ret == -ENODEV) + return 0; + + return ret; +} + +static void __exit cleanup_trusted(void) +{ + static_call(trusted_key_exit)(); +} + +late_initcall(init_trusted); +module_exit(cleanup_trusted); + +MODULE_LICENSE("GPL"); diff --git a/security/keys/trusted-keys/trusted_tee.c b/security/keys/trusted-keys/trusted_tee.c new file mode 100644 index 000000000000..2ce66c199e1d --- /dev/null +++ b/security/keys/trusted-keys/trusted_tee.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019-2021 Linaro Ltd. + * + * Author: + * Sumit Garg <sumit.garg@linaro.org> + */ + +#include <linux/err.h> +#include <linux/key-type.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/tee_drv.h> +#include <linux/uuid.h> + +#include <keys/trusted_tee.h> + +#define DRIVER_NAME "trusted-key-tee" + +/* + * Get random data for symmetric key + * + * [out] memref[0] Random data + */ +#define TA_CMD_GET_RANDOM 0x0 + +/* + * Seal trusted key using hardware unique key + * + * [in] memref[0] Plain key + * [out] memref[1] Sealed key datablob + */ +#define TA_CMD_SEAL 0x1 + +/* + * Unseal trusted key using hardware unique key + * + * [in] memref[0] Sealed key datablob + * [out] memref[1] Plain key + */ +#define TA_CMD_UNSEAL 0x2 + +/** + * struct trusted_key_tee_private - TEE Trusted key private data + * @dev: TEE based Trusted key device. + * @ctx: TEE context handler. + * @session_id: Trusted key TA session identifier. + * @shm_pool: Memory pool shared with TEE device. + */ +struct trusted_key_tee_private { + struct device *dev; + struct tee_context *ctx; + u32 session_id; + struct tee_shm *shm_pool; +}; + +static struct trusted_key_tee_private pvt_data; + +/* + * Have the TEE seal(encrypt) the symmetric key + */ +static int trusted_tee_seal(struct trusted_key_payload *p, char *datablob) +{ + int ret; + struct tee_ioctl_invoke_arg inv_arg; + struct tee_param param[4]; + struct tee_shm *reg_shm_in = NULL, *reg_shm_out = NULL; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + reg_shm_in = tee_shm_register(pvt_data.ctx, (unsigned long)p->key, + p->key_len, TEE_SHM_DMA_BUF | + TEE_SHM_KERNEL_MAPPED); + if (IS_ERR(reg_shm_in)) { + dev_err(pvt_data.dev, "key shm register failed\n"); + return PTR_ERR(reg_shm_in); + } + + reg_shm_out = tee_shm_register(pvt_data.ctx, (unsigned long)p->blob, + sizeof(p->blob), TEE_SHM_DMA_BUF | + TEE_SHM_KERNEL_MAPPED); + if (IS_ERR(reg_shm_out)) { + dev_err(pvt_data.dev, "blob shm register failed\n"); + ret = PTR_ERR(reg_shm_out); + goto out; + } + + inv_arg.func = TA_CMD_SEAL; + inv_arg.session = pvt_data.session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[0].u.memref.shm = reg_shm_in; + param[0].u.memref.size = p->key_len; + param[0].u.memref.shm_offs = 0; + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[1].u.memref.shm = reg_shm_out; + param[1].u.memref.size = sizeof(p->blob); + param[1].u.memref.shm_offs = 0; + + ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); + if ((ret < 0) || (inv_arg.ret != 0)) { + dev_err(pvt_data.dev, "TA_CMD_SEAL invoke err: %x\n", + inv_arg.ret); + ret = -EFAULT; + } else { + p->blob_len = param[1].u.memref.size; + } + +out: + if (reg_shm_out) + tee_shm_free(reg_shm_out); + if (reg_shm_in) + tee_shm_free(reg_shm_in); + + return ret; +} + +/* + * Have the TEE unseal(decrypt) the symmetric key + */ +static int trusted_tee_unseal(struct trusted_key_payload *p, char *datablob) +{ + int ret; + struct tee_ioctl_invoke_arg inv_arg; + struct tee_param param[4]; + struct tee_shm *reg_shm_in = NULL, *reg_shm_out = NULL; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + reg_shm_in = tee_shm_register(pvt_data.ctx, (unsigned long)p->blob, + p->blob_len, TEE_SHM_DMA_BUF | + TEE_SHM_KERNEL_MAPPED); + if (IS_ERR(reg_shm_in)) { + dev_err(pvt_data.dev, "blob shm register failed\n"); + return PTR_ERR(reg_shm_in); + } + + reg_shm_out = tee_shm_register(pvt_data.ctx, (unsigned long)p->key, + sizeof(p->key), TEE_SHM_DMA_BUF | + TEE_SHM_KERNEL_MAPPED); + if (IS_ERR(reg_shm_out)) { + dev_err(pvt_data.dev, "key shm register failed\n"); + ret = PTR_ERR(reg_shm_out); + goto out; + } + + inv_arg.func = TA_CMD_UNSEAL; + inv_arg.session = pvt_data.session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[0].u.memref.shm = reg_shm_in; + param[0].u.memref.size = p->blob_len; + param[0].u.memref.shm_offs = 0; + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[1].u.memref.shm = reg_shm_out; + param[1].u.memref.size = sizeof(p->key); + param[1].u.memref.shm_offs = 0; + + ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); + if ((ret < 0) || (inv_arg.ret != 0)) { + dev_err(pvt_data.dev, "TA_CMD_UNSEAL invoke err: %x\n", + inv_arg.ret); + ret = -EFAULT; + } else { + p->key_len = param[1].u.memref.size; + } + +out: + if (reg_shm_out) + tee_shm_free(reg_shm_out); + if (reg_shm_in) + tee_shm_free(reg_shm_in); + + return ret; +} + +/* + * Have the TEE generate random symmetric key + */ +static int trusted_tee_get_random(unsigned char *key, size_t key_len) +{ + int ret; + struct tee_ioctl_invoke_arg inv_arg; + struct tee_param param[4]; + struct tee_shm *reg_shm = NULL; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + reg_shm = tee_shm_register(pvt_data.ctx, (unsigned long)key, key_len, + TEE_SHM_DMA_BUF | TEE_SHM_KERNEL_MAPPED); + if (IS_ERR(reg_shm)) { + dev_err(pvt_data.dev, "key shm register failed\n"); + return PTR_ERR(reg_shm); + } + + inv_arg.func = TA_CMD_GET_RANDOM; + inv_arg.session = pvt_data.session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[0].u.memref.shm = reg_shm; + param[0].u.memref.size = key_len; + param[0].u.memref.shm_offs = 0; + + ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); + if ((ret < 0) || (inv_arg.ret != 0)) { + dev_err(pvt_data.dev, "TA_CMD_GET_RANDOM invoke err: %x\n", + inv_arg.ret); + ret = -EFAULT; + } else { + ret = param[0].u.memref.size; + } + + tee_shm_free(reg_shm); + + return ret; +} + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + if (ver->impl_id == TEE_IMPL_ID_OPTEE) + return 1; + else + return 0; +} + +static int trusted_key_probe(struct device *dev) +{ + struct tee_client_device *rng_device = to_tee_client_device(dev); + int ret; + struct tee_ioctl_open_session_arg sess_arg; + + memset(&sess_arg, 0, sizeof(sess_arg)); + + pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, + NULL); + if (IS_ERR(pvt_data.ctx)) + return -ENODEV; + + memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + sess_arg.num_params = 0; + + ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL); + if ((ret < 0) || (sess_arg.ret != 0)) { + dev_err(dev, "tee_client_open_session failed, err: %x\n", + sess_arg.ret); + ret = -EINVAL; + goto out_ctx; + } + pvt_data.session_id = sess_arg.session; + + ret = register_key_type(&key_type_trusted); + if (ret < 0) + goto out_sess; + + pvt_data.dev = dev; + + return 0; + +out_sess: + tee_client_close_session(pvt_data.ctx, pvt_data.session_id); +out_ctx: + tee_client_close_context(pvt_data.ctx); + + return ret; +} + +static int trusted_key_remove(struct device *dev) +{ + unregister_key_type(&key_type_trusted); + tee_client_close_session(pvt_data.ctx, pvt_data.session_id); + tee_client_close_context(pvt_data.ctx); + + return 0; +} + +static const struct tee_client_device_id trusted_key_id_table[] = { + {UUID_INIT(0xf04a0fe7, 0x1f5d, 0x4b9b, + 0xab, 0xf7, 0x61, 0x9b, 0x85, 0xb4, 0xce, 0x8c)}, + {} +}; +MODULE_DEVICE_TABLE(tee, trusted_key_id_table); + +static struct tee_client_driver trusted_key_driver = { + .id_table = trusted_key_id_table, + .driver = { + .name = DRIVER_NAME, + .bus = &tee_bus_type, + .probe = trusted_key_probe, + .remove = trusted_key_remove, + }, +}; + +static int trusted_tee_init(void) +{ + return driver_register(&trusted_key_driver.driver); +} + +static void trusted_tee_exit(void) +{ + driver_unregister(&trusted_key_driver.driver); +} + +struct trusted_key_ops trusted_key_tee_ops = { + .migratable = 0, /* non-migratable */ + .init = trusted_tee_init, + .seal = trusted_tee_seal, + .unseal = trusted_tee_unseal, + .get_random = trusted_tee_get_random, + .exit = trusted_tee_exit, +}; diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 493eb91ed017..469394550801 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -1,29 +1,22 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2010 IBM Corporation - * - * Author: - * David Safford <safford@us.ibm.com> + * Copyright (c) 2019-2021, Linaro Limited * * See Documentation/security/keys/trusted-encrypted.rst */ #include <crypto/hash_info.h> -#include <linux/uaccess.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/parser.h> #include <linux/string.h> #include <linux/err.h> -#include <keys/user-type.h> #include <keys/trusted-type.h> #include <linux/key-type.h> -#include <linux/rcupdate.h> #include <linux/crypto.h> #include <crypto/hash.h> #include <crypto/sha1.h> -#include <linux/capability.h> #include <linux/tpm.h> #include <linux/tpm_command.h> @@ -63,7 +56,7 @@ static int TSS_sha1(const unsigned char *data, unsigned int datalen, sdesc = init_sdesc(hashalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); + pr_info("can't alloc %s\n", hash_alg); return PTR_ERR(sdesc); } @@ -83,7 +76,7 @@ static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, sdesc = init_sdesc(hmacalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hmac_alg); + pr_info("can't alloc %s\n", hmac_alg); return PTR_ERR(sdesc); } @@ -136,7 +129,7 @@ int TSS_authhmac(unsigned char *digest, const unsigned char *key, sdesc = init_sdesc(hashalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); + pr_info("can't alloc %s\n", hash_alg); return PTR_ERR(sdesc); } @@ -212,7 +205,7 @@ int TSS_checkhmac1(unsigned char *buffer, sdesc = init_sdesc(hashalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); + pr_info("can't alloc %s\n", hash_alg); return PTR_ERR(sdesc); } ret = crypto_shash_init(&sdesc->shash); @@ -305,7 +298,7 @@ static int TSS_checkhmac2(unsigned char *buffer, sdesc = init_sdesc(hashalg); if (IS_ERR(sdesc)) { - pr_info("trusted_key: can't alloc %s\n", hash_alg); + pr_info("can't alloc %s\n", hash_alg); return PTR_ERR(sdesc); } ret = crypto_shash_init(&sdesc->shash); @@ -597,12 +590,12 @@ static int tpm_unseal(struct tpm_buf *tb, /* sessions for unsealing key and data */ ret = oiap(tb, &authhandle1, enonce1); if (ret < 0) { - pr_info("trusted_key: oiap failed (%d)\n", ret); + pr_info("oiap failed (%d)\n", ret); return ret; } ret = oiap(tb, &authhandle2, enonce2); if (ret < 0) { - pr_info("trusted_key: oiap failed (%d)\n", ret); + pr_info("oiap failed (%d)\n", ret); return ret; } @@ -612,7 +605,7 @@ static int tpm_unseal(struct tpm_buf *tb, return ret; if (ret != TPM_NONCE_SIZE) { - pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); + pr_info("tpm_get_random failed (%d)\n", ret); return -EIO; } ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, @@ -641,7 +634,7 @@ static int tpm_unseal(struct tpm_buf *tb, ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); if (ret < 0) { - pr_info("trusted_key: authhmac failed (%d)\n", ret); + pr_info("authhmac failed (%d)\n", ret); return ret; } @@ -653,7 +646,7 @@ static int tpm_unseal(struct tpm_buf *tb, *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, 0); if (ret < 0) { - pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret); + pr_info("TSS_checkhmac2 failed (%d)\n", ret); return ret; } memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen); @@ -680,7 +673,7 @@ static int key_seal(struct trusted_key_payload *p, p->key, p->key_len + 1, p->blob, &p->blob_len, o->blobauth, o->pcrinfo, o->pcrinfo_len); if (ret < 0) - pr_info("trusted_key: srkseal failed (%d)\n", ret); + pr_info("srkseal failed (%d)\n", ret); tpm_buf_destroy(&tb); return ret; @@ -702,7 +695,7 @@ static int key_unseal(struct trusted_key_payload *p, ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len, o->blobauth, p->key, &p->key_len); if (ret < 0) - pr_info("trusted_key: srkunseal failed (%d)\n", ret); + pr_info("srkunseal failed (%d)\n", ret); else /* pull migratable flag out of sealed key */ p->migratable = p->key[--p->key_len]; @@ -713,7 +706,6 @@ static int key_unseal(struct trusted_key_payload *p, enum { Opt_err, - Opt_new, Opt_load, Opt_update, Opt_keyhandle, Opt_keyauth, Opt_blobauth, Opt_pcrinfo, Opt_pcrlock, Opt_migratable, Opt_hash, @@ -722,9 +714,6 @@ enum { }; static const match_table_t key_tokens = { - {Opt_new, "new"}, - {Opt_load, "load"}, - {Opt_update, "update"}, {Opt_keyhandle, "keyhandle=%s"}, {Opt_keyauth, "keyauth=%s"}, {Opt_blobauth, "blobauth=%s"}, @@ -758,6 +747,9 @@ static int getoptions(char *c, struct trusted_key_payload *pay, opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1; + if (!c) + return 0; + while ((p = strsep(&c, " \t"))) { if (*p == '\0' || *p == ' ' || *p == '\t') continue; @@ -791,13 +783,33 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; break; case Opt_blobauth: - if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE) - return -EINVAL; - res = hex2bin(opt->blobauth, args[0].from, - SHA1_DIGEST_SIZE); - if (res < 0) - return -EINVAL; + /* + * TPM 1.2 authorizations are sha1 hashes passed in as + * hex strings. TPM 2.0 authorizations are simple + * passwords (although it can take a hash as well) + */ + opt->blobauth_len = strlen(args[0].from); + + if (opt->blobauth_len == 2 * TPM_DIGEST_SIZE) { + res = hex2bin(opt->blobauth, args[0].from, + TPM_DIGEST_SIZE); + if (res < 0) + return -EINVAL; + + opt->blobauth_len = TPM_DIGEST_SIZE; + break; + } + + if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) { + memcpy(opt->blobauth, args[0].from, + opt->blobauth_len); + break; + } + + return -EINVAL; + break; + case Opt_migratable: if (*args[0].from == '0') pay->migratable = 0; @@ -822,7 +834,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay, if (i == HASH_ALGO__LAST) return -EINVAL; if (!tpm2 && i != HASH_ALGO_SHA1) { - pr_info("trusted_key: TPM 1.x only supports SHA-1.\n"); + pr_info("TPM 1.x only supports SHA-1.\n"); return -EINVAL; } break; @@ -851,71 +863,6 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return 0; } -/* - * datablob_parse - parse the keyctl data and fill in the - * payload and options structures - * - * On success returns 0, otherwise -EINVAL. - */ -static int datablob_parse(char *datablob, struct trusted_key_payload *p, - struct trusted_key_options *o) -{ - substring_t args[MAX_OPT_ARGS]; - long keylen; - int ret = -EINVAL; - int key_cmd; - char *c; - - /* main command */ - c = strsep(&datablob, " \t"); - if (!c) - return -EINVAL; - key_cmd = match_token(c, key_tokens, args); - switch (key_cmd) { - case Opt_new: - /* first argument is key size */ - c = strsep(&datablob, " \t"); - if (!c) - return -EINVAL; - ret = kstrtol(c, 10, &keylen); - if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE) - return -EINVAL; - p->key_len = keylen; - ret = getoptions(datablob, p, o); - if (ret < 0) - return ret; - ret = Opt_new; - break; - case Opt_load: - /* first argument is sealed blob */ - c = strsep(&datablob, " \t"); - if (!c) - return -EINVAL; - p->blob_len = strlen(c) / 2; - if (p->blob_len > MAX_BLOB_SIZE) - return -EINVAL; - ret = hex2bin(p->blob, c, p->blob_len); - if (ret < 0) - return -EINVAL; - ret = getoptions(datablob, p, o); - if (ret < 0) - return ret; - ret = Opt_load; - break; - case Opt_update: - /* all arguments are options */ - ret = getoptions(datablob, p, o); - if (ret < 0) - return ret; - ret = Opt_update; - break; - case Opt_err: - return -EINVAL; - break; - } - return ret; -} - static struct trusted_key_options *trusted_options_alloc(void) { struct trusted_key_options *options; @@ -936,252 +883,99 @@ static struct trusted_key_options *trusted_options_alloc(void) return options; } -static struct trusted_key_payload *trusted_payload_alloc(struct key *key) +static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob) { - struct trusted_key_payload *p = NULL; - int ret; - - ret = key_payload_reserve(key, sizeof *p); - if (ret < 0) - return p; - p = kzalloc(sizeof *p, GFP_KERNEL); - if (p) - p->migratable = 1; /* migratable by default */ - return p; -} - -/* - * trusted_instantiate - create a new trusted key - * - * Unseal an existing trusted blob or, for a new key, get a - * random key, then seal and create a trusted key-type key, - * adding it to the specified keyring. - * - * On success, return 0. Otherwise return errno. - */ -static int trusted_instantiate(struct key *key, - struct key_preparsed_payload *prep) -{ - struct trusted_key_payload *payload = NULL; struct trusted_key_options *options = NULL; - size_t datalen = prep->datalen; - char *datablob; int ret = 0; - int key_cmd; - size_t key_len; int tpm2; tpm2 = tpm_is_tpm2(chip); if (tpm2 < 0) return tpm2; - if (datalen <= 0 || datalen > 32767 || !prep->data) - return -EINVAL; - - datablob = kmalloc(datalen + 1, GFP_KERNEL); - if (!datablob) + options = trusted_options_alloc(); + if (!options) return -ENOMEM; - memcpy(datablob, prep->data, datalen); - datablob[datalen] = '\0'; - options = trusted_options_alloc(); - if (!options) { - ret = -ENOMEM; - goto out; - } - payload = trusted_payload_alloc(key); - if (!payload) { - ret = -ENOMEM; + ret = getoptions(datablob, p, options); + if (ret < 0) goto out; - } + dump_options(options); - key_cmd = datablob_parse(datablob, payload, options); - if (key_cmd < 0) { - ret = key_cmd; + if (!options->keyhandle && !tpm2) { + ret = -EINVAL; goto out; } - if (!options->keyhandle) { - ret = -EINVAL; + if (tpm2) + ret = tpm2_seal_trusted(chip, p, options); + else + ret = key_seal(p, options); + if (ret < 0) { + pr_info("key_seal failed (%d)\n", ret); goto out; } - dump_payload(payload); - dump_options(options); - - switch (key_cmd) { - case Opt_load: - if (tpm2) - ret = tpm2_unseal_trusted(chip, payload, options); - else - ret = key_unseal(payload, options); - dump_payload(payload); - dump_options(options); - if (ret < 0) - pr_info("trusted_key: key_unseal failed (%d)\n", ret); - break; - case Opt_new: - key_len = payload->key_len; - ret = tpm_get_random(chip, payload->key, key_len); - if (ret < 0) - goto out; - - if (ret != key_len) { - pr_info("trusted_key: key_create failed (%d)\n", ret); - ret = -EIO; + if (options->pcrlock) { + ret = pcrlock(options->pcrlock); + if (ret < 0) { + pr_info("pcrlock failed (%d)\n", ret); goto out; } - if (tpm2) - ret = tpm2_seal_trusted(chip, payload, options); - else - ret = key_seal(payload, options); - if (ret < 0) - pr_info("trusted_key: key_seal failed (%d)\n", ret); - break; - default: - ret = -EINVAL; - goto out; } - if (!ret && options->pcrlock) - ret = pcrlock(options->pcrlock); out: - kfree_sensitive(datablob); kfree_sensitive(options); - if (!ret) - rcu_assign_keypointer(key, payload); - else - kfree_sensitive(payload); return ret; } -static void trusted_rcu_free(struct rcu_head *rcu) +static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob) { - struct trusted_key_payload *p; - - p = container_of(rcu, struct trusted_key_payload, rcu); - kfree_sensitive(p); -} - -/* - * trusted_update - reseal an existing key with new PCR values - */ -static int trusted_update(struct key *key, struct key_preparsed_payload *prep) -{ - struct trusted_key_payload *p; - struct trusted_key_payload *new_p; - struct trusted_key_options *new_o; - size_t datalen = prep->datalen; - char *datablob; + struct trusted_key_options *options = NULL; int ret = 0; + int tpm2; - if (key_is_negative(key)) - return -ENOKEY; - p = key->payload.data[0]; - if (!p->migratable) - return -EPERM; - if (datalen <= 0 || datalen > 32767 || !prep->data) - return -EINVAL; + tpm2 = tpm_is_tpm2(chip); + if (tpm2 < 0) + return tpm2; - datablob = kmalloc(datalen + 1, GFP_KERNEL); - if (!datablob) + options = trusted_options_alloc(); + if (!options) return -ENOMEM; - new_o = trusted_options_alloc(); - if (!new_o) { - ret = -ENOMEM; - goto out; - } - new_p = trusted_payload_alloc(key); - if (!new_p) { - ret = -ENOMEM; - goto out; - } - memcpy(datablob, prep->data, datalen); - datablob[datalen] = '\0'; - ret = datablob_parse(datablob, new_p, new_o); - if (ret != Opt_update) { - ret = -EINVAL; - kfree_sensitive(new_p); + ret = getoptions(datablob, p, options); + if (ret < 0) goto out; - } + dump_options(options); - if (!new_o->keyhandle) { + if (!options->keyhandle && !tpm2) { ret = -EINVAL; - kfree_sensitive(new_p); goto out; } - /* copy old key values, and reseal with new pcrs */ - new_p->migratable = p->migratable; - new_p->key_len = p->key_len; - memcpy(new_p->key, p->key, p->key_len); - dump_payload(p); - dump_payload(new_p); + if (tpm2) + ret = tpm2_unseal_trusted(chip, p, options); + else + ret = key_unseal(p, options); + if (ret < 0) + pr_info("key_unseal failed (%d)\n", ret); - ret = key_seal(new_p, new_o); - if (ret < 0) { - pr_info("trusted_key: key_seal failed (%d)\n", ret); - kfree_sensitive(new_p); - goto out; - } - if (new_o->pcrlock) { - ret = pcrlock(new_o->pcrlock); + if (options->pcrlock) { + ret = pcrlock(options->pcrlock); if (ret < 0) { - pr_info("trusted_key: pcrlock failed (%d)\n", ret); - kfree_sensitive(new_p); + pr_info("pcrlock failed (%d)\n", ret); goto out; } } - rcu_assign_keypointer(key, new_p); - call_rcu(&p->rcu, trusted_rcu_free); out: - kfree_sensitive(datablob); - kfree_sensitive(new_o); + kfree_sensitive(options); return ret; } -/* - * trusted_read - copy the sealed blob data to userspace in hex. - * On success, return to userspace the trusted key datablob size. - */ -static long trusted_read(const struct key *key, char *buffer, - size_t buflen) -{ - const struct trusted_key_payload *p; - char *bufp; - int i; - - p = dereference_key_locked(key); - if (!p) - return -EINVAL; - - if (buffer && buflen >= 2 * p->blob_len) { - bufp = buffer; - for (i = 0; i < p->blob_len; i++) - bufp = hex_byte_pack(bufp, p->blob[i]); - } - return 2 * p->blob_len; -} - -/* - * trusted_destroy - clear and free the key's payload - */ -static void trusted_destroy(struct key *key) +static int trusted_tpm_get_random(unsigned char *key, size_t key_len) { - kfree_sensitive(key->payload.data[0]); + return tpm_get_random(chip, key, key_len); } -struct key_type key_type_trusted = { - .name = "trusted", - .instantiate = trusted_instantiate, - .update = trusted_update, - .destroy = trusted_destroy, - .describe = user_describe, - .read = trusted_read, -}; - -EXPORT_SYMBOL_GPL(key_type_trusted); - static void trusted_shash_release(void) { if (hashalg) @@ -1196,14 +990,14 @@ static int __init trusted_shash_alloc(void) hmacalg = crypto_alloc_shash(hmac_alg, 0, 0); if (IS_ERR(hmacalg)) { - pr_info("trusted_key: could not allocate crypto %s\n", + pr_info("could not allocate crypto %s\n", hmac_alg); return PTR_ERR(hmacalg); } hashalg = crypto_alloc_shash(hash_alg, 0, 0); if (IS_ERR(hashalg)) { - pr_info("trusted_key: could not allocate crypto %s\n", + pr_info("could not allocate crypto %s\n", hash_alg); ret = PTR_ERR(hashalg); goto hashalg_fail; @@ -1231,16 +1025,13 @@ static int __init init_digests(void) return 0; } -static int __init init_trusted(void) +static int __init trusted_tpm_init(void) { int ret; - /* encrypted_keys.ko depends on successful load of this module even if - * TPM is not used. - */ chip = tpm_default_chip(); if (!chip) - return 0; + return -ENODEV; ret = init_digests(); if (ret < 0) @@ -1261,7 +1052,7 @@ err_put: return ret; } -static void __exit cleanup_trusted(void) +static void trusted_tpm_exit(void) { if (chip) { put_device(&chip->dev); @@ -1271,7 +1062,11 @@ static void __exit cleanup_trusted(void) } } -late_initcall(init_trusted); -module_exit(cleanup_trusted); - -MODULE_LICENSE("GPL"); +struct trusted_key_ops trusted_key_tpm_ops = { + .migratable = 1, /* migratable by default */ + .init = trusted_tpm_init, + .seal = trusted_tpm_seal, + .unseal = trusted_tpm_unseal, + .get_random = trusted_tpm_get_random, + .exit = trusted_tpm_exit, +}; diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index e2a0ed5d02f0..617fabd4d913 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -4,6 +4,8 @@ * Copyright (C) 2014 Intel Corporation */ +#include <linux/asn1_encoder.h> +#include <linux/oid_registry.h> #include <linux/string.h> #include <linux/err.h> #include <linux/tpm.h> @@ -12,6 +14,10 @@ #include <keys/trusted-type.h> #include <keys/trusted_tpm.h> +#include <asm/unaligned.h> + +#include "tpm2key.asn1.h" + static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SHA1, TPM_ALG_SHA1}, {HASH_ALGO_SHA256, TPM_ALG_SHA256}, @@ -20,6 +26,165 @@ static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, }; +static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 }; + +static int tpm2_key_encode(struct trusted_key_payload *payload, + struct trusted_key_options *options, + u8 *src, u32 len) +{ + const int SCRATCH_SIZE = PAGE_SIZE; + u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL); + u8 *work = scratch, *work1; + u8 *end_work = scratch + SCRATCH_SIZE; + u8 *priv, *pub; + u16 priv_len, pub_len; + + priv_len = get_unaligned_be16(src) + 2; + priv = src; + + src += priv_len; + + pub_len = get_unaligned_be16(src) + 2; + pub = src; + + if (!scratch) + return -ENOMEM; + + work = asn1_encode_oid(work, end_work, tpm2key_oid, + asn1_oid_len(tpm2key_oid)); + + if (options->blobauth_len == 0) { + unsigned char bool[3], *w = bool; + /* tag 0 is emptyAuth */ + w = asn1_encode_boolean(w, w + sizeof(bool), true); + if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) + return PTR_ERR(w); + work = asn1_encode_tag(work, end_work, 0, bool, w - bool); + } + + /* + * Assume both octet strings will encode to a 2 byte definite length + * + * Note: For a well behaved TPM, this warning should never + * trigger, so if it does there's something nefarious going on + */ + if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE, + "BUG: scratch buffer is too small")) + return -EINVAL; + + work = asn1_encode_integer(work, end_work, options->keyhandle); + work = asn1_encode_octet_string(work, end_work, pub, pub_len); + work = asn1_encode_octet_string(work, end_work, priv, priv_len); + + work1 = payload->blob; + work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob), + scratch, work - scratch); + if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) + return PTR_ERR(work1); + + return work1 - payload->blob; +} + +struct tpm2_key_context { + u32 parent; + const u8 *pub; + u32 pub_len; + const u8 *priv; + u32 priv_len; +}; + +static int tpm2_key_decode(struct trusted_key_payload *payload, + struct trusted_key_options *options, + u8 **buf) +{ + int ret; + struct tpm2_key_context ctx; + u8 *blob; + + memset(&ctx, 0, sizeof(ctx)); + + ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob, + payload->blob_len); + if (ret < 0) + return ret; + + if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE) + return -EINVAL; + + blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL); + if (!blob) + return -ENOMEM; + + *buf = blob; + options->keyhandle = ctx.parent; + + memcpy(blob, ctx.priv, ctx.priv_len); + blob += ctx.priv_len; + + memcpy(blob, ctx.pub, ctx.pub_len); + + return 0; +} + +int tpm2_key_parent(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_context *ctx = context; + const u8 *v = value; + int i; + + ctx->parent = 0; + for (i = 0; i < vlen; i++) { + ctx->parent <<= 8; + ctx->parent |= v[i]; + } + + return 0; +} + +int tpm2_key_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + enum OID oid = look_up_OID(value, vlen); + + if (oid != OID_TPMSealedData) { + char buffer[50]; + + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_debug("OID is \"%s\" which is not TPMSealedData\n", + buffer); + return -EINVAL; + } + + return 0; +} + +int tpm2_key_pub(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_context *ctx = context; + + ctx->pub = value; + ctx->pub_len = vlen; + + return 0; +} + +int tpm2_key_priv(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2_key_context *ctx = context; + + ctx->priv = value; + ctx->priv_len = vlen; + + return 0; +} + /** * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. * @@ -63,9 +228,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options) { - unsigned int blob_len; + int blob_len = 0; struct tpm_buf buf; u32 hash; + u32 flags; int i; int rc; @@ -79,7 +245,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip, if (i == ARRAY_SIZE(tpm2_hash_map)) return -EINVAL; - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); + if (!options->keyhandle) + return -EINVAL; + + rc = tpm_try_get_ops(chip); if (rc) return rc; @@ -97,29 +266,32 @@ int tpm2_seal_trusted(struct tpm_chip *chip, TPM_DIGEST_SIZE); /* sensitive */ - tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1); + tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len); + + tpm_buf_append_u16(&buf, options->blobauth_len); + if (options->blobauth_len) + tpm_buf_append(&buf, options->blobauth, options->blobauth_len); - tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE); - tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE); - tpm_buf_append_u16(&buf, payload->key_len + 1); + tpm_buf_append_u16(&buf, payload->key_len); tpm_buf_append(&buf, payload->key, payload->key_len); - tpm_buf_append_u8(&buf, payload->migratable); /* public */ tpm_buf_append_u16(&buf, 14 + options->policydigest_len); tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); tpm_buf_append_u16(&buf, hash); + /* key properties */ + flags = 0; + flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH; + flags |= payload->migratable ? (TPM2_OA_FIXED_TPM | + TPM2_OA_FIXED_PARENT) : 0; + tpm_buf_append_u32(&buf, flags); + /* policy */ - if (options->policydigest_len) { - tpm_buf_append_u32(&buf, 0); - tpm_buf_append_u16(&buf, options->policydigest_len); + tpm_buf_append_u16(&buf, options->policydigest_len); + if (options->policydigest_len) tpm_buf_append(&buf, options->policydigest, options->policydigest_len); - } else { - tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH); - tpm_buf_append_u16(&buf, 0); - } /* public parameters */ tpm_buf_append_u16(&buf, TPM_ALG_NULL); @@ -150,8 +322,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); - payload->blob_len = blob_len; + blob_len = tpm2_key_encode(payload, options, + &buf.data[TPM_HEADER_SIZE + 4], + blob_len); out: tpm_buf_destroy(&buf); @@ -162,6 +335,10 @@ out: else rc = -EPERM; } + if (blob_len < 0) + return blob_len; + + payload->blob_len = blob_len; tpm_put_ops(chip); return rc; @@ -189,13 +366,45 @@ static int tpm2_load_cmd(struct tpm_chip *chip, unsigned int private_len; unsigned int public_len; unsigned int blob_len; + u8 *blob, *pub; int rc; + u32 attrs; + + rc = tpm2_key_decode(payload, options, &blob); + if (rc) { + /* old form */ + blob = payload->blob; + payload->old_format = 1; + } + + /* new format carries keyhandle but old format doesn't */ + if (!options->keyhandle) + return -EINVAL; - private_len = be16_to_cpup((__be16 *) &payload->blob[0]); - if (private_len > (payload->blob_len - 2)) + /* must be big enough for at least the two be16 size counts */ + if (payload->blob_len < 4) + return -EINVAL; + + private_len = get_unaligned_be16(blob); + + /* must be big enough for following public_len */ + if (private_len + 2 + 2 > (payload->blob_len)) + return -E2BIG; + + public_len = get_unaligned_be16(blob + 2 + private_len); + if (private_len + 2 + public_len + 2 > payload->blob_len) return -E2BIG; - public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); + pub = blob + 2 + private_len + 2; + /* key attributes are always at offset 4 */ + attrs = get_unaligned_be32(pub + 4); + + if ((attrs & (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT)) == + (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT)) + payload->migratable = 0; + else + payload->migratable = 1; + blob_len = private_len + public_len + 4; if (blob_len > payload->blob_len) return -E2BIG; @@ -211,7 +420,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, options->keyauth /* hmac */, TPM_DIGEST_SIZE); - tpm_buf_append(&buf, payload->blob, blob_len); + tpm_buf_append(&buf, blob, blob_len); if (buf.flags & TPM_BUF_OVERFLOW) { rc = -E2BIG; @@ -224,6 +433,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip, (__be32 *) &buf.data[TPM_HEADER_SIZE]); out: + if (blob != payload->blob) + kfree(blob); tpm_buf_destroy(&buf); if (rc > 0) @@ -265,7 +476,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, NULL /* nonce */, 0, TPM2_SA_CONTINUE_SESSION, options->blobauth /* hmac */, - TPM_DIGEST_SIZE); + options->blobauth_len); rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); if (rc > 0) @@ -274,7 +485,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, if (!rc) { data_len = be16_to_cpup( (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); - if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) { + if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE) { rc = -EFAULT; goto out; } @@ -285,9 +496,19 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, } data = &buf.data[TPM_HEADER_SIZE + 6]; - memcpy(payload->key, data, data_len - 1); - payload->key_len = data_len - 1; - payload->migratable = data[data_len - 1]; + if (payload->old_format) { + /* migratable flag is at the end of the key */ + memcpy(payload->key, data, data_len - 1); + payload->key_len = data_len - 1; + payload->migratable = data[data_len - 1]; + } else { + /* + * migratable flag already collected from key + * attributes + */ + memcpy(payload->key, data, data_len); + payload->key_len = data_len; + } } out: diff --git a/security/security.c b/security/security.c index 5ac96b16f8fa..94383f83ba42 100644 --- a/security/security.c +++ b/security/security.c @@ -890,6 +890,13 @@ int security_sb_eat_lsm_opts(char *options, void **mnt_opts) } EXPORT_SYMBOL(security_sb_eat_lsm_opts); +int security_sb_mnt_opts_compat(struct super_block *sb, + void *mnt_opts) +{ + return call_int_hook(sb_mnt_opts_compat, 0, sb, mnt_opts); +} +EXPORT_SYMBOL(security_sb_mnt_opts_compat); + int security_sb_remount(struct super_block *sb, void *mnt_opts) { @@ -1762,12 +1769,19 @@ int security_task_getsid(struct task_struct *p) return call_int_hook(task_getsid, 0, p); } -void security_task_getsecid(struct task_struct *p, u32 *secid) +void security_task_getsecid_subj(struct task_struct *p, u32 *secid) +{ + *secid = 0; + call_void_hook(task_getsecid_subj, p, secid); +} +EXPORT_SYMBOL(security_task_getsecid_subj); + +void security_task_getsecid_obj(struct task_struct *p, u32 *secid) { *secid = 0; - call_void_hook(task_getsecid, p, secid); + call_void_hook(task_getsecid_obj, p, secid); } -EXPORT_SYMBOL(security_task_getsecid); +EXPORT_SYMBOL(security_task_getsecid_obj); int security_task_setnice(struct task_struct *p, int nice) { diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ddd097790d47..92f909a2e8f7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -230,9 +230,22 @@ static inline u32 cred_sid(const struct cred *cred) } /* + * get the subjective security ID of a task + */ +static inline u32 task_sid_subj(const struct task_struct *task) +{ + u32 sid; + + rcu_read_lock(); + sid = cred_sid(rcu_dereference(task->cred)); + rcu_read_unlock(); + return sid; +} + +/* * get the objective security ID of a task */ -static inline u32 task_sid(const struct task_struct *task) +static inline u32 task_sid_obj(const struct task_struct *task) { u32 sid; @@ -242,6 +255,29 @@ static inline u32 task_sid(const struct task_struct *task) return sid; } +/* + * get the security ID of a task for use with binder + */ +static inline u32 task_sid_binder(const struct task_struct *task) +{ + /* + * In many case where this function is used we should be using the + * task's subjective SID, but we can't reliably access the subjective + * creds of a task other than our own so we must use the objective + * creds/SID, which are safe to access. The downside is that if a task + * is temporarily overriding it's creds it will not be reflected here; + * however, it isn't clear that binder would handle that case well + * anyway. + * + * If this ever changes and we can safely reference the subjective + * creds/SID of another task, this function will make it easier to + * identify the various places where we make use of the task SIDs in + * the binder code. It is also likely that we will need to adjust + * the main drivers/android binder code as well. + */ + return task_sid_obj(task); +} + static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); /* @@ -760,7 +796,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, if (sb->s_user_ns != &init_user_ns && strcmp(sb->s_type->name, "tmpfs") && strcmp(sb->s_type->name, "ramfs") && - strcmp(sb->s_type->name, "devpts")) { + strcmp(sb->s_type->name, "devpts") && + strcmp(sb->s_type->name, "overlay")) { if (context_sid || fscontext_sid || rootcontext_sid || defcontext_sid) { rc = -EACCES; @@ -2034,11 +2071,8 @@ static inline u32 open_file_to_av(struct file *file) static int selinux_binder_set_context_mgr(struct task_struct *mgr) { - u32 mysid = current_sid(); - u32 mgrsid = task_sid(mgr); - return avc_has_perm(&selinux_state, - mysid, mgrsid, SECCLASS_BINDER, + current_sid(), task_sid_binder(mgr), SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); } @@ -2046,8 +2080,7 @@ static int selinux_binder_transaction(struct task_struct *from, struct task_struct *to) { u32 mysid = current_sid(); - u32 fromsid = task_sid(from); - u32 tosid = task_sid(to); + u32 fromsid = task_sid_binder(from); int rc; if (mysid != fromsid) { @@ -2058,19 +2091,16 @@ static int selinux_binder_transaction(struct task_struct *from, return rc; } - return avc_has_perm(&selinux_state, - fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, - NULL); + return avc_has_perm(&selinux_state, fromsid, task_sid_binder(to), + SECCLASS_BINDER, BINDER__CALL, NULL); } static int selinux_binder_transfer_binder(struct task_struct *from, struct task_struct *to) { - u32 fromsid = task_sid(from); - u32 tosid = task_sid(to); - return avc_has_perm(&selinux_state, - fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, + task_sid_binder(from), task_sid_binder(to), + SECCLASS_BINDER, BINDER__TRANSFER, NULL); } @@ -2078,7 +2108,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) { - u32 sid = task_sid(to); + u32 sid = task_sid_binder(to); struct file_security_struct *fsec = selinux_file(file); struct dentry *dentry = file->f_path.dentry; struct inode_security_struct *isec; @@ -2114,10 +2144,10 @@ static int selinux_binder_transfer_file(struct task_struct *from, } static int selinux_ptrace_access_check(struct task_struct *child, - unsigned int mode) + unsigned int mode) { u32 sid = current_sid(); - u32 csid = task_sid(child); + u32 csid = task_sid_obj(child); if (mode & PTRACE_MODE_READ) return avc_has_perm(&selinux_state, @@ -2130,15 +2160,15 @@ static int selinux_ptrace_access_check(struct task_struct *child, static int selinux_ptrace_traceme(struct task_struct *parent) { return avc_has_perm(&selinux_state, - task_sid(parent), current_sid(), SECCLASS_PROCESS, - PROCESS__PTRACE, NULL); + task_sid_subj(parent), task_sid_obj(current), + SECCLASS_PROCESS, PROCESS__PTRACE, NULL); } static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(target), SECCLASS_PROCESS, + current_sid(), task_sid_obj(target), SECCLASS_PROCESS, PROCESS__GETCAP, NULL); } @@ -2263,7 +2293,7 @@ static u32 ptrace_parent_sid(void) rcu_read_lock(); tracer = ptrace_parent(current); if (tracer) - sid = task_sid(tracer); + sid = task_sid_obj(tracer); rcu_read_unlock(); return sid; @@ -2684,6 +2714,61 @@ free_opt: return rc; } +static int selinux_sb_mnt_opts_compat(struct super_block *sb, void *mnt_opts) +{ + struct selinux_mnt_opts *opts = mnt_opts; + struct superblock_security_struct *sbsec = sb->s_security; + u32 sid; + int rc; + + /* + * Superblock not initialized (i.e. no options) - reject if any + * options specified, otherwise accept. + */ + if (!(sbsec->flags & SE_SBINITIALIZED)) + return opts ? 1 : 0; + + /* + * Superblock initialized and no options specified - reject if + * superblock has any options set, otherwise accept. + */ + if (!opts) + return (sbsec->flags & SE_MNTMASK) ? 1 : 0; + + if (opts->fscontext) { + rc = parse_sid(sb, opts->fscontext, &sid); + if (rc) + return 1; + if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid)) + return 1; + } + if (opts->context) { + rc = parse_sid(sb, opts->context, &sid); + if (rc) + return 1; + if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid)) + return 1; + } + if (opts->rootcontext) { + struct inode_security_struct *root_isec; + + root_isec = backing_inode_security(sb->s_root); + rc = parse_sid(sb, opts->rootcontext, &sid); + if (rc) + return 1; + if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid)) + return 1; + } + if (opts->defcontext) { + rc = parse_sid(sb, opts->defcontext, &sid); + if (rc) + return 1; + if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid)) + return 1; + } + return 0; +} + static int selinux_sb_remount(struct super_block *sb, void *mnt_opts) { struct selinux_mnt_opts *opts = mnt_opts; @@ -3920,7 +4005,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { struct file *file; - u32 sid = task_sid(tsk); + u32 sid = task_sid_obj(tsk); u32 perm; struct file_security_struct *fsec; @@ -4139,47 +4224,52 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents) static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETPGID, NULL); } static int selinux_task_getpgid(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__GETPGID, NULL); } static int selinux_task_getsid(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__GETSESSION, NULL); } -static void selinux_task_getsecid(struct task_struct *p, u32 *secid) +static void selinux_task_getsecid_subj(struct task_struct *p, u32 *secid) +{ + *secid = task_sid_subj(p); +} + +static void selinux_task_getsecid_obj(struct task_struct *p, u32 *secid) { - *secid = task_sid(p); + *secid = task_sid_obj(p); } static int selinux_task_setnice(struct task_struct *p, int nice) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_setioprio(struct task_struct *p, int ioprio) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_getioprio(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__GETSCHED, NULL); } @@ -4210,7 +4300,7 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, upon context transitions. See selinux_bprm_committing_creds. */ if (old_rlim->rlim_max != new_rlim->rlim_max) return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL); return 0; @@ -4219,21 +4309,21 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, static int selinux_task_setscheduler(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } static int selinux_task_getscheduler(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__GETSCHED, NULL); } static int selinux_task_movememory(struct task_struct *p) { return avc_has_perm(&selinux_state, - current_sid(), task_sid(p), SECCLASS_PROCESS, + current_sid(), task_sid_obj(p), SECCLASS_PROCESS, PROCESS__SETSCHED, NULL); } @@ -4252,14 +4342,14 @@ static int selinux_task_kill(struct task_struct *p, struct kernel_siginfo *info, else secid = cred_sid(cred); return avc_has_perm(&selinux_state, - secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); + secid, task_sid_obj(p), SECCLASS_PROCESS, perm, NULL); } static void selinux_task_to_inode(struct task_struct *p, struct inode *inode) { struct inode_security_struct *isec = selinux_inode(inode); - u32 sid = task_sid(p); + u32 sid = task_sid_obj(p); spin_lock(&isec->lock); isec->sclass = inode_mode_to_security_class(inode->i_mode); @@ -6152,7 +6242,7 @@ static int selinux_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *m struct ipc_security_struct *isec; struct msg_security_struct *msec; struct common_audit_data ad; - u32 sid = task_sid(target); + u32 sid = task_sid_subj(target); int rc; isec = selinux_ipc(msq); @@ -7077,6 +7167,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security), LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts), + LSM_HOOK_INIT(sb_mnt_opts_compat, selinux_sb_mnt_opts_compat), LSM_HOOK_INIT(sb_remount, selinux_sb_remount), LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount), LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options), @@ -7148,7 +7239,8 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid), LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid), LSM_HOOK_INIT(task_getsid, selinux_task_getsid), - LSM_HOOK_INIT(task_getsecid, selinux_task_getsecid), + LSM_HOOK_INIT(task_getsecid_subj, selinux_task_getsecid_subj), + LSM_HOOK_INIT(task_getsecid_obj, selinux_task_getsecid_obj), LSM_HOOK_INIT(task_setnice, selinux_task_setnice), LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio), LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio), diff --git a/security/selinux/ima.c b/security/selinux/ima.c index 03715893ff97..34d421861bfc 100644 --- a/security/selinux/ima.c +++ b/security/selinux/ima.c @@ -13,18 +13,83 @@ #include "ima.h" /* - * selinux_ima_measure_state - Measure hash of the SELinux policy + * selinux_ima_collect_state - Read selinux configuration settings * - * @state: selinux state struct + * @state: selinux_state * - * NOTE: This function must be called with policy_mutex held. + * On success returns the configuration settings string. + * On error, returns NULL. */ -void selinux_ima_measure_state(struct selinux_state *state) +static char *selinux_ima_collect_state(struct selinux_state *state) { + const char *on = "=1;", *off = "=0;"; + char *buf; + int buf_len, len, i, rc; + + buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;") + 1; + + len = strlen(on); + for (i = 0; i < __POLICYDB_CAPABILITY_MAX; i++) + buf_len += strlen(selinux_policycap_names[i]) + len; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return NULL; + + rc = strscpy(buf, "initialized", buf_len); + WARN_ON(rc < 0); + + rc = strlcat(buf, selinux_initialized(state) ? on : off, buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, "enforcing", buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, enforcing_enabled(state) ? on : off, buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, "checkreqprot", buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, checkreqprot_get(state) ? on : off, buf_len); + WARN_ON(rc >= buf_len); + + for (i = 0; i < __POLICYDB_CAPABILITY_MAX; i++) { + rc = strlcat(buf, selinux_policycap_names[i], buf_len); + WARN_ON(rc >= buf_len); + + rc = strlcat(buf, state->policycap[i] ? on : off, buf_len); + WARN_ON(rc >= buf_len); + } + + return buf; +} + +/* + * selinux_ima_measure_state_locked - Measure SELinux state and hash of policy + * + * @state: selinux state struct + */ +void selinux_ima_measure_state_locked(struct selinux_state *state) +{ + char *state_str = NULL; void *policy = NULL; size_t policy_len; int rc = 0; + WARN_ON(!mutex_is_locked(&state->policy_mutex)); + + state_str = selinux_ima_collect_state(state); + if (!state_str) { + pr_err("SELinux: %s: failed to read state.\n", __func__); + return; + } + + ima_measure_critical_data("selinux", "selinux-state", + state_str, strlen(state_str), false); + + kfree(state_str); + /* * Measure SELinux policy only after initialization is completed. */ @@ -42,3 +107,17 @@ void selinux_ima_measure_state(struct selinux_state *state) vfree(policy); } + +/* + * selinux_ima_measure_state - Measure SELinux state and hash of policy + * + * @state: selinux state struct + */ +void selinux_ima_measure_state(struct selinux_state *state) +{ + WARN_ON(mutex_is_locked(&state->policy_mutex)); + + mutex_lock(&state->policy_mutex); + selinux_ima_measure_state_locked(state); + mutex_unlock(&state->policy_mutex); +} diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index ba2e01a6955c..62d19bccf3de 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -242,11 +242,12 @@ struct security_class_mapping secclass_map[] = { { "infiniband_endport", { "manage_subnet", NULL } }, { "bpf", - {"map_create", "map_read", "map_write", "prog_load", "prog_run"} }, + { "map_create", "map_read", "map_write", "prog_load", "prog_run", + NULL } }, { "xdp_socket", { COMMON_SOCK_PERMS, NULL } }, { "perf_event", - {"open", "cpu", "kernel", "tracepoint", "read", "write"} }, + { "open", "cpu", "kernel", "tracepoint", "read", "write", NULL } }, { "lockdown", { "integrity", "confidentiality", NULL } }, { "anon_inode", diff --git a/security/selinux/include/ima.h b/security/selinux/include/ima.h index d69c36611423..75ca92b4a462 100644 --- a/security/selinux/include/ima.h +++ b/security/selinux/include/ima.h @@ -15,10 +15,16 @@ #ifdef CONFIG_IMA extern void selinux_ima_measure_state(struct selinux_state *selinux_state); +extern void selinux_ima_measure_state_locked( + struct selinux_state *selinux_state); #else static inline void selinux_ima_measure_state(struct selinux_state *selinux_state) { } +static inline void selinux_ima_measure_state_locked( + struct selinux_state *selinux_state) +{ +} #endif #endif /* _SELINUX_IMA_H_ */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 7650de048570..ac0ece01305a 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -426,7 +426,7 @@ extern struct page *selinux_kernel_status_page(struct selinux_state *state); #define SELINUX_KERNEL_STATUS_VERSION 1 struct selinux_kernel_status { - u32 version; /* version number of thie structure */ + u32 version; /* version number of the structure */ u32 sequence; /* sequence number of seqlock logic */ u32 enforcing; /* current setting of enforcing mode */ u32 policyload; /* times of policy reloaded */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index fff6babeeae6..e4cd7cb856f3 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -41,6 +41,7 @@ #include "security.h" #include "objsec.h" #include "conditional.h" +#include "ima.h" enum sel_inos { SEL_ROOT_INO = 2, @@ -182,6 +183,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, selinux_status_update_setenforce(state, new_value); if (!new_value) call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL); + + selinux_ima_measure_state(state); } length = count; out: @@ -758,6 +761,9 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, checkreqprot_set(fsi->state, (new_value ? 1 : 0)); length = count; + + selinux_ima_measure_state(fsi->state); + out: kfree(page); return length; diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 3881787ce492..b8f6b3e0a921 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -13,7 +13,7 @@ static struct kmem_cache *hashtab_node_cachep __ro_after_init; /* * Here we simply round the number of elements up to the nearest power of two. - * I tried also other options like rouding down or rounding to the closest + * I tried also other options like rounding down or rounding to the closest * power of two (up or down based on which is closer), but I was unable to * find any significant difference in lookup/insert performance that would * justify switching to a different (less intuitive) formula. It could be that diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 301633145040..f0ba82611343 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2202,7 +2202,7 @@ static void selinux_notify_policy_change(struct selinux_state *state, selinux_status_update_policyload(state, seqno); selinux_netlbl_cache_invalidate(); selinux_xfrm_notify_policyload(); - selinux_ima_measure_state(state); + selinux_ima_measure_state_locked(state); } void selinux_policy_commit(struct selinux_state *state, diff --git a/security/smack/smack.h b/security/smack/smack.h index a9768b12716b..08f9cb80655c 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -383,7 +383,23 @@ static inline struct smack_known *smk_of_task(const struct task_smack *tsp) return tsp->smk_task; } -static inline struct smack_known *smk_of_task_struct( +static inline struct smack_known *smk_of_task_struct_subj( + const struct task_struct *t) +{ + struct smack_known *skp; + const struct cred *cred; + + rcu_read_lock(); + + cred = rcu_dereference(t->cred); + skp = smk_of_task(smack_cred(cred)); + + rcu_read_unlock(); + + return skp; +} + +static inline struct smack_known *smk_of_task_struct_obj( const struct task_struct *t) { struct smack_known *skp; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 12a45e61c1a5..cd14bec4ad80 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -159,7 +159,7 @@ static int smk_bu_current(char *note, struct smack_known *oskp, static int smk_bu_task(struct task_struct *otp, int mode, int rc) { struct task_smack *tsp = smack_cred(current_cred()); - struct smack_known *smk_task = smk_of_task_struct(otp); + struct smack_known *smk_task = smk_of_task_struct_obj(otp); char acc[SMK_NUM_ACCESS_TYPE + 1]; if (rc <= 0) @@ -479,7 +479,7 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { struct smack_known *skp; - skp = smk_of_task_struct(ctp); + skp = smk_of_task_struct_obj(ctp); return smk_ptrace_rule_check(current, skp, mode, __func__); } @@ -2033,7 +2033,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access, const char *caller) { struct smk_audit_info ad; - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *skp = smk_of_task_struct_subj(p); int rc; smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); @@ -2078,15 +2078,29 @@ static int smack_task_getsid(struct task_struct *p) } /** - * smack_task_getsecid - get the secid of the task - * @p: the object task + * smack_task_getsecid_subj - get the subjective secid of the task + * @p: the task * @secid: where to put the result * - * Sets the secid to contain a u32 version of the smack label. + * Sets the secid to contain a u32 version of the task's subjective smack label. + */ +static void smack_task_getsecid_subj(struct task_struct *p, u32 *secid) +{ + struct smack_known *skp = smk_of_task_struct_subj(p); + + *secid = skp->smk_secid; +} + +/** + * smack_task_getsecid_obj - get the objective secid of the task + * @p: the task + * @secid: where to put the result + * + * Sets the secid to contain a u32 version of the task's objective smack label. */ -static void smack_task_getsecid(struct task_struct *p, u32 *secid) +static void smack_task_getsecid_obj(struct task_struct *p, u32 *secid) { - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *skp = smk_of_task_struct_obj(p); *secid = skp->smk_secid; } @@ -2174,7 +2188,7 @@ static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info, { struct smk_audit_info ad; struct smack_known *skp; - struct smack_known *tkp = smk_of_task_struct(p); + struct smack_known *tkp = smk_of_task_struct_obj(p); int rc; if (!sig) @@ -2212,7 +2226,7 @@ static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info, static void smack_task_to_inode(struct task_struct *p, struct inode *inode) { struct inode_smack *isp = smack_inode(inode); - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *skp = smk_of_task_struct_obj(p); isp->smk_inode = skp; isp->smk_flags |= SMK_INODE_INSTANT; @@ -3483,7 +3497,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ static int smack_getprocattr(struct task_struct *p, char *name, char **value) { - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *skp = smk_of_task_struct_subj(p); char *cp; int slen; @@ -4759,7 +4773,8 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_setpgid, smack_task_setpgid), LSM_HOOK_INIT(task_getpgid, smack_task_getpgid), LSM_HOOK_INIT(task_getsid, smack_task_getsid), - LSM_HOOK_INIT(task_getsecid, smack_task_getsecid), + LSM_HOOK_INIT(task_getsecid_subj, smack_task_getsecid_subj), + LSM_HOOK_INIT(task_getsecid_obj, smack_task_getsecid_obj), LSM_HOOK_INIT(task_setnice, smack_task_setnice), LSM_HOOK_INIT(task_setioprio, smack_task_setioprio), LSM_HOOK_INIT(task_getioprio, smack_task_getioprio), |