From f4dc37785e9b3373d0cb93125d5579fed2af3a43 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 22 Oct 2015 21:26:10 +0300 Subject: integrity: define '.evm' as a builtin 'trusted' keyring Require all keys added to the EVM keyring be signed by an existing trusted key on the system trusted keyring. This patch also switches IMA to use integrity_init_keyring(). Changes in v3: * Added 'init_keyring' config based variable to skip initializing keyring instead of using __integrity_init_keyring() wrapper. * Added dependency back to CONFIG_IMA_TRUSTED_KEYRING Changes in v2: * Replace CONFIG_EVM_TRUSTED_KEYRING with IMA and EVM common CONFIG_INTEGRITY_TRUSTED_KEYRING configuration option * Deprecate CONFIG_IMA_TRUSTED_KEYRING but keep it for config file compatibility. (Mimi Zohar) Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/Kconfig | 11 +++++++++++ security/integrity/digsig.c | 14 ++++++++++++-- security/integrity/evm/evm_main.c | 8 +++++--- security/integrity/ima/Kconfig | 5 ++++- security/integrity/ima/ima.h | 12 ------------ security/integrity/ima/ima_init.c | 2 +- security/integrity/integrity.h | 5 ++--- 7 files changed, 35 insertions(+), 22 deletions(-) (limited to 'security') diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 73c457bf5a4a..21d756832b75 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -41,6 +41,17 @@ config INTEGRITY_ASYMMETRIC_KEYS This option enables digital signature verification using asymmetric keys. +config INTEGRITY_TRUSTED_KEYRING + bool "Require all keys on the integrity keyrings be signed" + depends on SYSTEM_TRUSTED_KEYRING + depends on INTEGRITY_ASYMMETRIC_KEYS + select KEYS_DEBUG_PROC_KEYS + default y + help + This option requires that all keys added to the .ima and + .evm keyrings be signed by a key on the system trusted + keyring. + config INTEGRITY_AUDIT bool "Enables integrity auditing support " depends on AUDIT diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 5be9ffbe90ba..8ef15118cc78 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -24,15 +24,22 @@ static struct key *keyring[INTEGRITY_KEYRING_MAX]; static const char *keyring_name[INTEGRITY_KEYRING_MAX] = { +#ifndef CONFIG_INTEGRITY_TRUSTED_KEYRING "_evm", - "_module", -#ifndef CONFIG_IMA_TRUSTED_KEYRING "_ima", #else + ".evm", ".ima", #endif + "_module", }; +#ifdef CONFIG_INTEGRITY_TRUSTED_KEYRING +static bool init_keyring __initdata = true; +#else +static bool init_keyring __initdata; +#endif + int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen) { @@ -68,6 +75,9 @@ int __init integrity_init_keyring(const unsigned int id) const struct cred *cred = current_cred(); int err = 0; + if (!init_keyring) + return 0; + keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), KGIDT_INIT(0), cred, ((KEY_POS_ALL & ~KEY_POS_SETATTR) | diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 1334e02ae8f4..75b7e3031d2a 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -478,15 +478,17 @@ static int __init init_evm(void) evm_init_config(); + error = integrity_init_keyring(INTEGRITY_KEYRING_EVM); + if (error) + return error; + error = evm_init_secfs(); if (error < 0) { pr_info("Error registering secfs\n"); - goto err; + return error; } return 0; -err: - return error; } /* diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index df303346029b..a292b881c16f 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -123,14 +123,17 @@ config IMA_APPRAISE If unsure, say N. config IMA_TRUSTED_KEYRING - bool "Require all keys on the .ima keyring be signed" + bool "Require all keys on the .ima keyring be signed (deprecated)" depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING depends on INTEGRITY_ASYMMETRIC_KEYS + select INTEGRITY_TRUSTED_KEYRING default y help This option requires that all keys added to the .ima keyring be signed by a key on the system trusted keyring. + This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING + config IMA_LOAD_X509 bool "Load X509 certificate onto the '.ima' trusted keyring" depends on IMA_TRUSTED_KEYRING diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e2a60c30df44..9e82367f5190 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -251,16 +251,4 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, return -EINVAL; } #endif /* CONFIG_IMA_LSM_RULES */ - -#ifdef CONFIG_IMA_TRUSTED_KEYRING -static inline int ima_init_keyring(const unsigned int id) -{ - return integrity_init_keyring(id); -} -#else -static inline int ima_init_keyring(const unsigned int id) -{ - return 0; -} -#endif /* CONFIG_IMA_TRUSTED_KEYRING */ #endif diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index e600cadd231c..bd79f254d204 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -116,7 +116,7 @@ int __init ima_init(void) if (!ima_used_chip) pr_info("No TPM chip found, activating TPM-bypass!\n"); - rc = ima_init_keyring(INTEGRITY_KEYRING_IMA); + rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA); if (rc) return rc; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 9c6168709d3b..07726a731727 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -125,8 +125,8 @@ int integrity_kernel_read(struct file *file, loff_t offset, int __init integrity_read_file(const char *path, char **data); #define INTEGRITY_KEYRING_EVM 0 -#define INTEGRITY_KEYRING_MODULE 1 -#define INTEGRITY_KEYRING_IMA 2 +#define INTEGRITY_KEYRING_IMA 1 +#define INTEGRITY_KEYRING_MODULE 2 #define INTEGRITY_KEYRING_MAX 3 #ifdef CONFIG_INTEGRITY_SIGNATURE @@ -149,7 +149,6 @@ static inline int integrity_init_keyring(const unsigned int id) { return 0; } - #endif /* CONFIG_INTEGRITY_SIGNATURE */ #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS -- cgit v1.2.3-55-g7522 From 79be093500791cc25cc31bcaec5a4db62e21497b Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Mon, 7 Dec 2015 14:34:32 -0800 Subject: Smack: File receive for sockets The existing file receive hook checks for access on the file inode even for UDS. This is not right, as the inode is not used by Smack to make access checks for sockets. This change checks for an appropriate access relationship between the receiving (current) process and the socket. If the process can't write to the socket's send label or the socket's receive label can't write to the process fail. This will allow the legitimate cases, where the socket sender and socket receiver can freely communicate. Only strangly set socket labels should cause a problem. Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'security') diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ff81026f6ddb..b20ef0602267 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1860,12 +1860,34 @@ static int smack_file_receive(struct file *file) int may = 0; struct smk_audit_info ad; struct inode *inode = file_inode(file); + struct socket *sock; + struct task_smack *tsp; + struct socket_smack *ssp; if (unlikely(IS_PRIVATE(inode))) return 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); + + if (S_ISSOCK(inode->i_mode)) { + sock = SOCKET_I(inode); + ssp = sock->sk->sk_security; + tsp = current_security(); + /* + * If the receiving process can't write to the + * passed socket or if the passed socket can't + * write to the receiving process don't accept + * the passed socket. + */ + rc = smk_access(tsp->smk_task, ssp->smk_out, MAY_WRITE, &ad); + rc = smk_bu_file(file, may, rc); + if (rc < 0) + return rc; + rc = smk_access(ssp->smk_in, tsp->smk_task, MAY_WRITE, &ad); + rc = smk_bu_file(file, may, rc); + return rc; + } /* * This code relies on bitmasks. */ -- cgit v1.2.3-55-g7522 From 2ce523eb8976a12de1a4fb6fe8ad0b09b5dafb31 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 22 Oct 2015 21:26:21 +0300 Subject: evm: load an x509 certificate from the kernel This patch defines a configuration option and the evm_load_x509() hook to load an X509 certificate onto the EVM trusted kernel keyring. Changes in v4: * Patch description updated Changes in v3: * Removed EVM_X509_PATH definition. CONFIG_EVM_X509_PATH is used directly. Changes in v2: * default key patch changed to /etc/keys Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/evm/Kconfig | 17 +++++++++++++++++ security/integrity/evm/evm_main.c | 7 +++++++ security/integrity/iint.c | 1 + security/integrity/integrity.h | 8 ++++++++ 4 files changed, 33 insertions(+) (limited to 'security') diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index bf19723cf117..913532c5db4f 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -42,3 +42,20 @@ config EVM_EXTRA_SMACK_XATTRS additional info to the calculation, requires existing EVM labeled file systems to be relabeled. +config EVM_LOAD_X509 + bool "Load an X509 certificate onto the '.evm' trusted keyring" + depends on INTEGRITY_TRUSTED_KEYRING + default n + help + Load an X509 certificate onto the '.evm' trusted keyring. + + This option enables X509 certificate loading from the kernel + onto the '.evm' trusted keyring. A public key can be used to + verify EVM integrity starting from the 'init' process. + +config EVM_X509_PATH + string "EVM X509 certificate path" + depends on EVM_LOAD_X509 + default "/etc/keys/x509_evm.der" + help + This option defines X509 certificate path. diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 75b7e3031d2a..519de0a0ba72 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -472,6 +472,13 @@ out: } EXPORT_SYMBOL_GPL(evm_inode_init_security); +#ifdef CONFIG_EVM_LOAD_X509 +void __init evm_load_x509(void) +{ + integrity_load_x509(INTEGRITY_KEYRING_EVM, CONFIG_EVM_X509_PATH); +} +#endif + static int __init init_evm(void) { int error; diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 3d2f5b45c8cb..2de9c820903f 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -254,4 +254,5 @@ out: void __init integrity_load_keys(void) { ima_load_x509(); + evm_load_x509(); } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 07726a731727..5efe2ecc538d 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -170,6 +170,14 @@ static inline void ima_load_x509(void) } #endif +#ifdef CONFIG_EVM_LOAD_X509 +void __init evm_load_x509(void); +#else +static inline void evm_load_x509(void) +{ +} +#endif + #ifdef CONFIG_INTEGRITY_AUDIT /* declarations */ void integrity_audit_msg(int audit_msgno, struct inode *inode, -- cgit v1.2.3-55-g7522 From 26ddabfe96bb7468763c9c92791404d991b16250 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 22 Oct 2015 21:26:26 +0300 Subject: evm: enable EVM when X509 certificate is loaded In order to enable EVM before starting the 'init' process, evm_initialized needs to be non-zero. Previously non-zero indicated that the HMAC key was loaded. When EVM loads the X509 before calling 'init', with this patch it is now possible to enable EVM to start signature based verification. This patch defines bits to enable EVM if a key of any type is loaded. Changes in v3: * print error message if key is not set Changes in v2: * EVM_STATE_KEY_SET replaced by EVM_INIT_HMAC * EVM_STATE_X509_SET replaced by EVM_INIT_X509 Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/evm/evm.h | 3 +++ security/integrity/evm/evm_crypto.c | 4 ++++ security/integrity/evm/evm_main.c | 6 +++++- security/integrity/evm/evm_secfs.c | 4 ++-- 4 files changed, 14 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 88bfe77efa1c..f5f12727771a 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -21,6 +21,9 @@ #include "../integrity.h" +#define EVM_INIT_HMAC 0x0001 +#define EVM_INIT_X509 0x0002 + extern int evm_initialized; extern char *evm_hmac; extern char *evm_hash; diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 461f8d891579..2c3591eca989 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -40,6 +40,10 @@ static struct shash_desc *init_desc(char type) struct shash_desc *desc; if (type == EVM_XATTR_HMAC) { + if (!(evm_initialized & EVM_INIT_HMAC)) { + pr_err("HMAC key is not set\n"); + return ERR_PTR(-ENOKEY); + } tfm = &hmac_tfm; algo = evm_hmac; } else { diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 519de0a0ba72..420d94da2793 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -475,7 +475,11 @@ EXPORT_SYMBOL_GPL(evm_inode_init_security); #ifdef CONFIG_EVM_LOAD_X509 void __init evm_load_x509(void) { - integrity_load_x509(INTEGRITY_KEYRING_EVM, CONFIG_EVM_X509_PATH); + int rc; + + rc = integrity_load_x509(INTEGRITY_KEYRING_EVM, CONFIG_EVM_X509_PATH); + if (!rc) + evm_initialized |= EVM_INIT_X509; } #endif diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index cf12a04717d3..3f775dfea868 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -64,7 +64,7 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, char temp[80]; int i, error; - if (!capable(CAP_SYS_ADMIN) || evm_initialized) + if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_INIT_HMAC)) return -EPERM; if (count >= sizeof(temp) || count == 0) @@ -80,7 +80,7 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, error = evm_init_key(); if (!error) { - evm_initialized = 1; + evm_initialized |= EVM_INIT_HMAC; pr_info("initialized\n"); } else pr_err("initialization failed\n"); -- cgit v1.2.3-55-g7522 From 7626676320f398980a6bb4490fd58e924c888f6a Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 22 Oct 2015 21:26:32 +0300 Subject: evm: provide a function to set the EVM key from the kernel A crypto HW kernel module can possibly initialize the EVM key from the kernel __init code to enable EVM before calling the 'init' process. This patch provides a function evm_set_key() to set the EVM key directly without using the KEY subsystem. Changes in v4: * kernel-doc style for evm_set_key Changes in v3: * error reporting moved to evm_set_key * EVM_INIT_HMAC moved to evm_set_key * added bitop to prevent key setting race Changes in v2: * use size_t for key size instead of signed int * provide EVM_MAX_KEY_SIZE macro in * provide EVM_MIN_KEY_SIZE macro in Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- include/linux/evm.h | 7 ++++++ security/integrity/evm/evm_crypto.c | 50 +++++++++++++++++++++++++++++++------ security/integrity/evm/evm_secfs.c | 10 +++----- 3 files changed, 53 insertions(+), 14 deletions(-) (limited to 'security') diff --git a/include/linux/evm.h b/include/linux/evm.h index 1fcb88ca88de..35ed9a8a403a 100644 --- a/include/linux/evm.h +++ b/include/linux/evm.h @@ -14,6 +14,7 @@ struct integrity_iint_cache; #ifdef CONFIG_EVM +extern int evm_set_key(void *key, size_t keylen); extern enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name, void *xattr_value, @@ -42,6 +43,12 @@ static inline int posix_xattr_acl(const char *xattrname) } #endif #else + +static inline int evm_set_key(void *key, size_t keylen) +{ + return -EOPNOTSUPP; +} + #ifdef CONFIG_INTEGRITY static inline enum integrity_status evm_verifyxattr(struct dentry *dentry, const char *xattr_name, diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 2c3591eca989..30b6b7d0429f 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include "evm.h" @@ -32,6 +33,44 @@ struct crypto_shash *hash_tfm; static DEFINE_MUTEX(mutex); +#define EVM_SET_KEY_BUSY 0 + +static unsigned long evm_set_key_flags; + +/** + * evm_set_key() - set EVM HMAC key from the kernel + * @key: pointer to a buffer with the key data + * @size: length of the key data + * + * This function allows setting the EVM HMAC key from the kernel + * without using the "encrypted" key subsystem keys. It can be used + * by the crypto HW kernel module which has its own way of managing + * keys. + * + * key length should be between 32 and 128 bytes long + */ +int evm_set_key(void *key, size_t keylen) +{ + int rc; + + rc = -EBUSY; + if (test_and_set_bit(EVM_SET_KEY_BUSY, &evm_set_key_flags)) + goto busy; + rc = -EINVAL; + if (keylen > MAX_KEY_SIZE) + goto inval; + memcpy(evmkey, key, keylen); + evm_initialized |= EVM_INIT_HMAC; + pr_info("key initialized\n"); + return 0; +inval: + clear_bit(EVM_SET_KEY_BUSY, &evm_set_key_flags); +busy: + pr_err("key initialization failed\n"); + return rc; +} +EXPORT_SYMBOL_GPL(evm_set_key); + static struct shash_desc *init_desc(char type) { long rc; @@ -244,7 +283,7 @@ int evm_init_key(void) { struct key *evm_key; struct encrypted_key_payload *ekp; - int rc = 0; + int rc; evm_key = request_key(&key_type_encrypted, EVMKEY, NULL); if (IS_ERR(evm_key)) @@ -252,12 +291,9 @@ int evm_init_key(void) down_read(&evm_key->sem); ekp = evm_key->payload.data[0]; - if (ekp->decrypted_datalen > MAX_KEY_SIZE) { - rc = -EINVAL; - goto out; - } - memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen); -out: + + rc = evm_set_key(ekp->decrypted_data, ekp->decrypted_datalen); + /* burn the original key contents */ memset(ekp->decrypted_data, 0, ekp->decrypted_datalen); up_read(&evm_key->sem); diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 3f775dfea868..c8dccd54d501 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -62,7 +62,7 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char temp[80]; - int i, error; + int i; if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_INIT_HMAC)) return -EPERM; @@ -78,12 +78,8 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, if ((sscanf(temp, "%d", &i) != 1) || (i != 1)) return -EINVAL; - error = evm_init_key(); - if (!error) { - evm_initialized |= EVM_INIT_HMAC; - pr_info("initialized\n"); - } else - pr_err("initialization failed\n"); + evm_init_key(); + return count; } -- cgit v1.2.3-55-g7522 From 523b74b16bcbba34c662da5df7fa111ae4c1d0e6 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 22 Oct 2015 21:26:42 +0300 Subject: evm: reset EVM status when file attributes change The EVM verification status is cached in iint->evm_status and if it was successful, never re-verified again when IMA passes the 'iint' to evm_verifyxattr(). When file attributes or extended attributes change, we may wish to re-verify EVM integrity as well. For example, after setting a digital signature we may need to re-verify the signature and update the iint->flags that there is an EVM signature. This patch enables that by resetting evm_status to INTEGRITY_UKNOWN state. Changes in v2: * Flag setting moved to EVM layer Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/evm/evm_main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'security') diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 420d94da2793..f7160253f17f 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -358,6 +358,15 @@ int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name) return evm_protect_xattr(dentry, xattr_name, NULL, 0); } +static void evm_reset_status(struct inode *inode) +{ + struct integrity_iint_cache *iint; + + iint = integrity_iint_find(inode); + if (iint) + iint->evm_status = INTEGRITY_UNKNOWN; +} + /** * evm_inode_post_setxattr - update 'security.evm' to reflect the changes * @dentry: pointer to the affected dentry @@ -378,6 +387,8 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, && !posix_xattr_acl(xattr_name))) return; + evm_reset_status(dentry->d_inode); + evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); } @@ -396,6 +407,8 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) if (!evm_initialized || !evm_protected_xattr(xattr_name)) return; + evm_reset_status(dentry->d_inode); + evm_update_evmxattr(dentry, xattr_name, NULL, 0); } -- cgit v1.2.3-55-g7522 From 05d3884b1ee66d83ad70ffa658c7b363797e2b0c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 27 Nov 2015 14:52:33 +0100 Subject: evm: EVM_LOAD_X509 depends on EVM The newly added EVM_LOAD_X509 code can be configured even if CONFIG_EVM is disabled, but that causes a link error: security/built-in.o: In function `integrity_load_keys': digsig_asymmetric.c:(.init.text+0x400): undefined reference to `evm_load_x509' This adds a Kconfig dependency to ensure it is only enabled when CONFIG_EVM is set as well. Signed-off-by: Arnd Bergmann Fixes: 2ce523eb8976 ("evm: load x509 certificate from the kernel") Signed-off-by: Mimi Zohar --- security/integrity/evm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index 913532c5db4f..e825e0ae78e7 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -44,7 +44,7 @@ config EVM_EXTRA_SMACK_XATTRS config EVM_LOAD_X509 bool "Load an X509 certificate onto the '.evm' trusted keyring" - depends on INTEGRITY_TRUSTED_KEYRING + depends on EVM && INTEGRITY_TRUSTED_KEYRING default n help Load an X509 certificate onto the '.evm' trusted keyring. -- cgit v1.2.3-55-g7522 From 38d859f991f3a05b352a06f82af0baa1acf33e02 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Wed, 2 Dec 2015 17:47:54 +0200 Subject: IMA: policy can now be updated multiple times The new rules get appended to the original policy, forming a queue. The new rules are first added to a temporary list, which on error get released without disturbing the normal IMA operations. On success both lists (the current policy and the new rules) are spliced. IMA policy reads are many orders of magnitude more numerous compared to writes, the match code is RCU protected. The updater side also does list splice in RCU manner. Signed-off-by: Petko Manolov Signed-off-by: Mimi Zohar --- security/integrity/ima/Kconfig | 11 ++++++ security/integrity/ima/ima_fs.c | 13 ++++++ security/integrity/ima/ima_policy.c | 79 ++++++++++++++++++++++++------------- 3 files changed, 75 insertions(+), 28 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index a292b881c16f..e74d66cbfe87 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -107,6 +107,17 @@ config IMA_DEFAULT_HASH default "sha512" if IMA_DEFAULT_HASH_SHA512 default "wp512" if IMA_DEFAULT_HASH_WP512 +config IMA_WRITE_POLICY + bool "Enable multiple writes to the IMA policy" + depends on IMA + default n + help + IMA policy can now be updated multiple times. The new rules get + appended to the original policy. Have in mind that the rules are + scanned in FIFO order so be careful when you design and add new ones. + + If unsure, say N. + config IMA_APPRAISE bool "Appraise integrity measurements" depends on IMA diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 816d175da79a..a3cf5c0ab501 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -25,6 +25,8 @@ #include "ima.h" +static DEFINE_MUTEX(ima_write_mutex); + static int valid_policy = 1; #define TMPBUFLEN 12 static ssize_t ima_show_htable_value(char __user *buf, size_t count, @@ -261,6 +263,11 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, { char *data = NULL; ssize_t result; + int res; + + res = mutex_lock_interruptible(&ima_write_mutex); + if (res) + return res; if (datalen >= PAGE_SIZE) datalen = PAGE_SIZE - 1; @@ -286,6 +293,8 @@ out: if (result < 0) valid_policy = 0; kfree(data); + mutex_unlock(&ima_write_mutex); + return result; } @@ -337,8 +346,12 @@ static int ima_release_policy(struct inode *inode, struct file *file) return 0; } ima_update_policy(); +#ifndef CONFIG_IMA_WRITE_POLICY securityfs_remove(ima_policy); ima_policy = NULL; +#else + clear_bit(IMA_FS_BUSY, &ima_fs_flags); +#endif return 0; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3997e206f82d..10a0a9b9e22d 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "ima.h" @@ -135,11 +136,11 @@ static struct ima_rule_entry default_appraise_rules[] = { static LIST_HEAD(ima_default_rules); static LIST_HEAD(ima_policy_rules); +static LIST_HEAD(ima_temp_rules); static struct list_head *ima_rules; -static DEFINE_MUTEX(ima_rules_mutex); - static int ima_policy __initdata; + static int __init default_measure_policy_setup(char *str) { if (ima_policy) @@ -171,21 +172,18 @@ static int __init default_appraise_policy_setup(char *str) __setup("ima_appraise_tcb", default_appraise_policy_setup); /* - * Although the IMA policy does not change, the LSM policy can be - * reloaded, leaving the IMA LSM based rules referring to the old, - * stale LSM policy. - * - * Update the IMA LSM based rules to reflect the reloaded LSM policy. - * We assume the rules still exist; and BUG_ON() if they don't. + * The LSM policy can be reloaded, leaving the IMA LSM based rules referring + * to the old, stale LSM policy. Update the IMA LSM based rules to reflect + * the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if + * they don't. */ static void ima_lsm_update_rules(void) { - struct ima_rule_entry *entry, *tmp; + struct ima_rule_entry *entry; int result; int i; - mutex_lock(&ima_rules_mutex); - list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { + list_for_each_entry(entry, &ima_policy_rules, list) { for (i = 0; i < MAX_LSM_RULES; i++) { if (!entry->lsm[i].rule) continue; @@ -196,7 +194,6 @@ static void ima_lsm_update_rules(void) BUG_ON(!entry->lsm[i].rule); } } - mutex_unlock(&ima_rules_mutex); } /** @@ -319,9 +316,9 @@ static int get_subaction(struct ima_rule_entry *rule, int func) * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type) * conditions. * - * (There is no need for locking when walking the policy list, - * as elements in the list are never deleted, nor does the list - * change.) + * Since the IMA policy may be updated multiple times we need to lock the + * list when walking it. Reads are many orders of magnitude more numerous + * than writes so ima_match_policy() is classical RCU candidate. */ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags) @@ -329,7 +326,8 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, struct ima_rule_entry *entry; int action = 0, actmask = flags | (flags << 1); - list_for_each_entry(entry, ima_rules, list) { + rcu_read_lock(); + list_for_each_entry_rcu(entry, ima_rules, list) { if (!(entry->action & actmask)) continue; @@ -351,6 +349,7 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, if (!actmask) break; } + rcu_read_unlock(); return action; } @@ -365,7 +364,6 @@ void ima_update_policy_flag(void) { struct ima_rule_entry *entry; - ima_policy_flag = 0; list_for_each_entry(entry, ima_rules, list) { if (entry->action & IMA_DO_MASK) ima_policy_flag |= entry->action; @@ -419,12 +417,36 @@ void __init ima_init_policy(void) * ima_update_policy - update default_rules with new measure rules * * Called on file .release to update the default rules with a complete new - * policy. Once updated, the policy is locked, no additional rules can be - * added to the policy. + * policy. What we do here is to splice ima_policy_rules and ima_temp_rules so + * they make a queue. The policy may be updated multiple times and this is the + * RCU updater. + * + * Policy rules are never deleted so ima_policy_flag gets zeroed only once when + * we switch from the default policy to user defined. */ void ima_update_policy(void) { - ima_rules = &ima_policy_rules; + struct list_head *first, *last, *policy; + + /* append current policy with the new rules */ + first = (&ima_temp_rules)->next; + last = (&ima_temp_rules)->prev; + policy = &ima_policy_rules; + + synchronize_rcu(); + + last->next = policy; + rcu_assign_pointer(list_next_rcu(policy->prev), first); + first->prev = policy->prev; + policy->prev = last; + + /* prepare for the next policy rules addition */ + INIT_LIST_HEAD(&ima_temp_rules); + + if (ima_rules != policy) { + ima_policy_flag = 0; + ima_rules = policy; + } ima_update_policy_flag(); } @@ -746,7 +768,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) * ima_parse_add_rule - add a rule to ima_policy_rules * @rule - ima measurement policy rule * - * Uses a mutex to protect the policy list from multiple concurrent writers. + * Avoid locking by allowing just one writer at a time in ima_write_policy() * Returns the length of the rule parsed, an error code on failure */ ssize_t ima_parse_add_rule(char *rule) @@ -782,26 +804,27 @@ ssize_t ima_parse_add_rule(char *rule) return result; } - mutex_lock(&ima_rules_mutex); - list_add_tail(&entry->list, &ima_policy_rules); - mutex_unlock(&ima_rules_mutex); + list_add_tail(&entry->list, &ima_temp_rules); return len; } -/* ima_delete_rules called to cleanup invalid policy */ +/** + * ima_delete_rules() called to cleanup invalid in-flight policy. + * We don't need locking as we operate on the temp list, which is + * different from the active one. There is also only one user of + * ima_delete_rules() at a time. + */ void ima_delete_rules(void) { struct ima_rule_entry *entry, *tmp; int i; - mutex_lock(&ima_rules_mutex); - list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { + list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) { for (i = 0; i < MAX_LSM_RULES; i++) kfree(entry->lsm[i].args_p); list_del(&entry->list); kfree(entry); } - mutex_unlock(&ima_rules_mutex); } -- cgit v1.2.3-55-g7522 From 41c89b64d7184a780f12f2cccdabe65cb2408893 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Wed, 2 Dec 2015 17:47:55 +0200 Subject: IMA: create machine owner and blacklist keyrings This option creates IMA MOK and blacklist keyrings. IMA MOK is an intermediate keyring that sits between .system and .ima keyrings, effectively forming a simple CA hierarchy. To successfully import a key into .ima_mok it must be signed by a key which CA is in .system keyring. On turn any key that needs to go in .ima keyring must be signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty at kernel boot. IMA blacklist keyring contains all revoked IMA keys. It is consulted before any other keyring. If the search is successful the requested operation is rejected and error is returned to the caller. Signed-off-by: Petko Manolov Signed-off-by: Mimi Zohar --- crypto/asymmetric_keys/x509_public_key.c | 2 ++ include/keys/system_keyring.h | 24 ++++++++++++++ security/integrity/digsig_asymmetric.c | 14 +++++++++ security/integrity/ima/Kconfig | 18 +++++++++++ security/integrity/ima/Makefile | 1 + security/integrity/ima/ima_mok.c | 54 ++++++++++++++++++++++++++++++++ 6 files changed, 113 insertions(+) create mode 100644 security/integrity/ima/ima_mok.c (limited to 'security') diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 2a44b3752471..9e9e5a6a9ed6 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -321,6 +321,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) goto error_free_cert; } else if (!prep->trusted) { ret = x509_validate_trust(cert, get_system_trusted_keyring()); + if (ret) + ret = x509_validate_trust(cert, get_ima_mok_keyring()); if (!ret) prep->trusted = 1; } diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index b20cd885c1fd..39fd38cfa8c9 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -35,4 +35,28 @@ extern int system_verify_data(const void *data, unsigned long len, enum key_being_used_for usage); #endif +#ifdef CONFIG_IMA_MOK_KEYRING +extern struct key *ima_mok_keyring; +extern struct key *ima_blacklist_keyring; + +static inline struct key *get_ima_mok_keyring(void) +{ + return ima_mok_keyring; +} +static inline struct key *get_ima_blacklist_keyring(void) +{ + return ima_blacklist_keyring; +} +#else +static inline struct key *get_ima_mok_keyring(void) +{ + return NULL; +} +static inline struct key *get_ima_blacklist_keyring(void) +{ + return NULL; +} +#endif /* CONFIG_IMA_MOK_KEYRING */ + + #endif /* _KEYS_SYSTEM_KEYRING_H */ diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 4fec1816a2b3..5ade2a7517a6 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "integrity.h" @@ -32,9 +33,22 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) pr_debug("key search: \"%s\"\n", name); + key = get_ima_blacklist_keyring(); + if (key) { + key_ref_t kref; + + kref = keyring_search(make_key_ref(key, 1), + &key_type_asymmetric, name); + if (!IS_ERR(kref)) { + pr_err("Key '%s' is in ima_blacklist_keyring\n", name); + return ERR_PTR(-EKEYREJECTED); + } + } + if (keyring) { /* search in specific keyring */ key_ref_t kref; + kref = keyring_search(make_key_ref(keyring, 1), &key_type_asymmetric, name); if (IS_ERR(kref)) diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index e74d66cbfe87..8d5e6e0e0937 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -145,6 +145,24 @@ config IMA_TRUSTED_KEYRING This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING +config IMA_MOK_KEYRING + bool "Create IMA machine owner keys (MOK) and blacklist keyrings" + depends on SYSTEM_TRUSTED_KEYRING + depends on IMA_TRUSTED_KEYRING + default n + help + This option creates IMA MOK and blacklist keyrings. IMA MOK is an + intermediate keyring that sits between .system and .ima keyrings, + effectively forming a simple CA hierarchy. To successfully import a + key into .ima_mok it must be signed by a key which CA is in .system + keyring. On turn any key that needs to go in .ima keyring must be + signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty + at kernel boot. + + IMA blacklist keyring contains all revoked IMA keys. It is consulted + before any other keyring. If the search is successful the requested + operation is rejected and error is returned to the caller. + config IMA_LOAD_X509 bool "Load X509 certificate onto the '.ima' trusted keyring" depends on IMA_TRUSTED_KEYRING diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index d79263d2fdbf..a8539f9e060f 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o +obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c new file mode 100644 index 000000000000..18e37f57f512 --- /dev/null +++ b/security/integrity/ima/ima_mok.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Juniper Networks, Inc. + * + * Author: + * Petko Manolov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +struct key *ima_mok_keyring; +struct key *ima_blacklist_keyring; + +/* + * Allocate the IMA MOK and blacklist keyrings + */ +__init int ima_mok_init(void) +{ + pr_notice("Allocating IMA MOK and blacklist keyrings.\n"); + + ima_mok_keyring = keyring_alloc(".ima_mok", + KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ | + KEY_USR_WRITE | KEY_USR_SEARCH, + KEY_ALLOC_NOT_IN_QUOTA, NULL); + + ima_blacklist_keyring = keyring_alloc(".ima_blacklist", + KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ | + KEY_USR_WRITE | KEY_USR_SEARCH, + KEY_ALLOC_NOT_IN_QUOTA, NULL); + + if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) + panic("Can't allocate IMA MOK or blacklist keyrings."); + set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags); + set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags); + return 0; +} + +module_init(ima_mok_init); -- cgit v1.2.3-55-g7522 From 80eae209d63ac6361c7b445f7e7e41f39c044772 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Wed, 2 Dec 2015 17:47:56 +0200 Subject: IMA: allow reading back the current IMA policy It is often useful to be able to read back the IMA policy. It is even more important after introducing CONFIG_IMA_WRITE_POLICY. This option allows the root user to see the current policy rules. Signed-off-by: Zbigniew Jasinski Signed-off-by: Petko Manolov Signed-off-by: Mimi Zohar --- security/integrity/ima/Kconfig | 10 ++ security/integrity/ima/ima.h | 15 ++- security/integrity/ima/ima_fs.c | 29 ++++- security/integrity/ima/ima_policy.c | 207 +++++++++++++++++++++++++++++++++++- 4 files changed, 253 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 8d5e6e0e0937..e54a8a8dae94 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -118,6 +118,16 @@ config IMA_WRITE_POLICY If unsure, say N. +config IMA_READ_POLICY + bool "Enable reading back the current IMA policy" + depends on IMA + default y if IMA_WRITE_POLICY + default n if !IMA_WRITE_POLICY + help + It is often useful to be able to read back the IMA policy. It is + even more important after introducing CONFIG_IMA_WRITE_POLICY. + This option allows the root user to see the current policy rules. + config IMA_APPRAISE bool "Appraise integrity measurements" depends on IMA diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 9e82367f5190..917407fb7e94 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -166,6 +166,10 @@ void ima_update_policy(void); void ima_update_policy_flag(void); ssize_t ima_parse_add_rule(char *); void ima_delete_rules(void); +void *ima_policy_start(struct seq_file *m, loff_t *pos); +void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos); +void ima_policy_stop(struct seq_file *m, void *v); +int ima_policy_show(struct seq_file *m, void *v); /* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 @@ -250,5 +254,12 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, { return -EINVAL; } -#endif /* CONFIG_IMA_LSM_RULES */ -#endif +#endif /* CONFIG_IMA_TRUSTED_KEYRING */ + +#ifdef CONFIG_IMA_READ_POLICY +#define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR) +#else +#define POLICY_FILE_FLAGS S_IWUSR +#endif /* CONFIG_IMA_WRITE_POLICY */ + +#endif /* __LINUX_IMA_H */ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index a3cf5c0ab501..eebb985fd083 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -311,14 +311,31 @@ enum ima_fs_flags { static unsigned long ima_fs_flags; +#ifdef CONFIG_IMA_READ_POLICY +static const struct seq_operations ima_policy_seqops = { + .start = ima_policy_start, + .next = ima_policy_next, + .stop = ima_policy_stop, + .show = ima_policy_show, +}; +#endif + /* * ima_open_policy: sequentialize access to the policy file */ static int ima_open_policy(struct inode *inode, struct file *filp) { - /* No point in being allowed to open it if you aren't going to write */ - if (!(filp->f_flags & O_WRONLY)) + if (!(filp->f_flags & O_WRONLY)) { +#ifndef CONFIG_IMA_READ_POLICY return -EACCES; +#else + if ((filp->f_flags & O_ACCMODE) != O_RDONLY) + return -EACCES; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return seq_open(filp, &ima_policy_seqops); +#endif + } if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags)) return -EBUSY; return 0; @@ -335,6 +352,9 @@ static int ima_release_policy(struct inode *inode, struct file *file) { const char *cause = valid_policy ? "completed" : "failed"; + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return 0; + pr_info("IMA: policy update %s\n", cause); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, "policy_update", cause, !valid_policy, 0); @@ -345,6 +365,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) clear_bit(IMA_FS_BUSY, &ima_fs_flags); return 0; } + ima_update_policy(); #ifndef CONFIG_IMA_WRITE_POLICY securityfs_remove(ima_policy); @@ -358,6 +379,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) static const struct file_operations ima_measure_policy_ops = { .open = ima_open_policy, .write = ima_write_policy, + .read = seq_read, .release = ima_release_policy, .llseek = generic_file_llseek, }; @@ -395,8 +417,7 @@ int __init ima_fs_init(void) if (IS_ERR(violations)) goto out; - ima_policy = securityfs_create_file("policy", - S_IWUSR, + ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, ima_dir, NULL, &ima_measure_policy_ops); if (IS_ERR(ima_policy)) diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 10a0a9b9e22d..2f4e0f5f31e2 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "ima.h" @@ -458,8 +459,8 @@ enum { Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, Opt_func, Opt_mask, Opt_fsmagic, - Opt_uid, Opt_euid, Opt_fowner, - Opt_appraise_type, Opt_fsuuid, Opt_permit_directio + Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner, + Opt_appraise_type, Opt_permit_directio }; static match_table_t policy_tokens = { @@ -828,3 +829,205 @@ void ima_delete_rules(void) kfree(entry); } } + +#ifdef CONFIG_IMA_READ_POLICY +enum { + mask_exec = 0, mask_write, mask_read, mask_append +}; + +static char *mask_tokens[] = { + "MAY_EXEC", + "MAY_WRITE", + "MAY_READ", + "MAY_APPEND" +}; + +enum { + func_file = 0, func_mmap, func_bprm, + func_module, func_firmware, func_post +}; + +static char *func_tokens[] = { + "FILE_CHECK", + "MMAP_CHECK", + "BPRM_CHECK", + "MODULE_CHECK", + "FIRMWARE_CHECK", + "POST_SETATTR" +}; + +void *ima_policy_start(struct seq_file *m, loff_t *pos) +{ + loff_t l = *pos; + struct ima_rule_entry *entry; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, ima_rules, list) { + if (!l--) { + rcu_read_unlock(); + return entry; + } + } + rcu_read_unlock(); + return NULL; +} + +void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct ima_rule_entry *entry = v; + + rcu_read_lock(); + entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list); + rcu_read_unlock(); + (*pos)++; + + return (&entry->list == ima_rules) ? NULL : entry; +} + +void ima_policy_stop(struct seq_file *m, void *v) +{ +} + +#define pt(token) policy_tokens[token + Opt_err].pattern +#define mt(token) mask_tokens[token] +#define ft(token) func_tokens[token] + +int ima_policy_show(struct seq_file *m, void *v) +{ + struct ima_rule_entry *entry = v; + int i = 0; + char tbuf[64] = {0,}; + + rcu_read_lock(); + + if (entry->action & MEASURE) + seq_puts(m, pt(Opt_measure)); + if (entry->action & DONT_MEASURE) + seq_puts(m, pt(Opt_dont_measure)); + if (entry->action & APPRAISE) + seq_puts(m, pt(Opt_appraise)); + if (entry->action & DONT_APPRAISE) + seq_puts(m, pt(Opt_dont_appraise)); + if (entry->action & AUDIT) + seq_puts(m, pt(Opt_audit)); + + seq_puts(m, " "); + + if (entry->flags & IMA_FUNC) { + switch (entry->func) { + case FILE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_file)); + break; + case MMAP_CHECK: + seq_printf(m, pt(Opt_func), ft(func_mmap)); + break; + case BPRM_CHECK: + seq_printf(m, pt(Opt_func), ft(func_bprm)); + break; + case MODULE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_module)); + break; + case FIRMWARE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_firmware)); + break; + case POST_SETATTR: + seq_printf(m, pt(Opt_func), ft(func_post)); + break; + default: + snprintf(tbuf, sizeof(tbuf), "%d", entry->func); + seq_printf(m, pt(Opt_func), tbuf); + break; + } + seq_puts(m, " "); + } + + if (entry->flags & IMA_MASK) { + if (entry->mask & MAY_EXEC) + seq_printf(m, pt(Opt_mask), mt(mask_exec)); + if (entry->mask & MAY_WRITE) + seq_printf(m, pt(Opt_mask), mt(mask_write)); + if (entry->mask & MAY_READ) + seq_printf(m, pt(Opt_mask), mt(mask_read)); + if (entry->mask & MAY_APPEND) + seq_printf(m, pt(Opt_mask), mt(mask_append)); + seq_puts(m, " "); + } + + if (entry->flags & IMA_FSMAGIC) { + snprintf(tbuf, sizeof(tbuf), "0x%lx", entry->fsmagic); + seq_printf(m, pt(Opt_fsmagic), tbuf); + seq_puts(m, " "); + } + + if (entry->flags & IMA_FSUUID) { + seq_puts(m, "fsuuid="); + for (i = 0; i < ARRAY_SIZE(entry->fsuuid); ++i) { + switch (i) { + case 4: + case 6: + case 8: + case 10: + seq_puts(m, "-"); + } + seq_printf(m, "%x", entry->fsuuid[i]); + } + seq_puts(m, " "); + } + + if (entry->flags & IMA_UID) { + snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); + seq_printf(m, pt(Opt_uid), tbuf); + seq_puts(m, " "); + } + + if (entry->flags & IMA_EUID) { + snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); + seq_printf(m, pt(Opt_euid), tbuf); + seq_puts(m, " "); + } + + if (entry->flags & IMA_FOWNER) { + snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); + seq_printf(m, pt(Opt_fowner), tbuf); + seq_puts(m, " "); + } + + for (i = 0; i < MAX_LSM_RULES; i++) { + if (entry->lsm[i].rule) { + switch (i) { + case LSM_OBJ_USER: + seq_printf(m, pt(Opt_obj_user), + (char *)entry->lsm[i].args_p); + break; + case LSM_OBJ_ROLE: + seq_printf(m, pt(Opt_obj_role), + (char *)entry->lsm[i].args_p); + break; + case LSM_OBJ_TYPE: + seq_printf(m, pt(Opt_obj_type), + (char *)entry->lsm[i].args_p); + break; + case LSM_SUBJ_USER: + seq_printf(m, pt(Opt_subj_user), + (char *)entry->lsm[i].args_p); + break; + case LSM_SUBJ_ROLE: + seq_printf(m, pt(Opt_subj_role), + (char *)entry->lsm[i].args_p); + break; + case LSM_SUBJ_TYPE: + seq_printf(m, pt(Opt_subj_type), + (char *)entry->lsm[i].args_p); + break; + } + } + } + if (entry->flags & IMA_DIGSIG_REQUIRED) + seq_puts(m, "appraise_type=imasig "); + if (entry->flags & IMA_PERMIT_DIRECTIO) + seq_puts(m, "permit_directio "); + rcu_read_unlock(); + seq_puts(m, "\n"); + return 0; +} +#endif /* CONFIG_IMA_READ_POLICY */ -- cgit v1.2.3-55-g7522 From d3600bcf9d64d88dc1d189a754dcfab960ce751f Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 10 Nov 2015 08:34:46 -0500 Subject: KEYS: prevent keys from being removed from specified keyrings Userspace should not be allowed to remove keys from certain keyrings (eg. blacklist), though the keys themselves can expire. This patch defines a new key flag named KEY_FLAG_KEEP to prevent userspace from being able to unlink, revoke, invalidate or timed out a key on a keyring. When this flag is set on the keyring, all keys subsequently added are flagged. In addition, when this flag is set, the keyring itself can not be cleared. Signed-off-by: Mimi Zohar Cc: David Howells --- include/linux/key.h | 1 + security/keys/key.c | 6 +++++- security/keys/keyctl.c | 56 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 11 deletions(-) (limited to 'security') diff --git a/include/linux/key.h b/include/linux/key.h index 66f705243985..7321ab8ef949 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -177,6 +177,7 @@ struct key { #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 */ #define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_KEEP 12 /* set if key should not be removed */ /* the key type and key description string * - the desc is used to match a key against search criteria diff --git a/security/keys/key.c b/security/keys/key.c index ab7997ded725..09ef276c4bdc 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -429,8 +429,12 @@ static int __key_instantiate_and_link(struct key *key, awaken = 1; /* and link it into the destination keyring */ - if (keyring) + if (keyring) { + if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) + set_bit(KEY_FLAG_KEEP, &key->flags); + __key_link(key, _edit); + } /* disable the authorisation key */ if (authkey) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index fb111eafcb89..e83ec6b9eb9d 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -358,11 +358,14 @@ error: * and any links to the key will be automatically garbage collected after a * certain amount of time (/proc/sys/kernel/keys/gc_delay). * + * Keys with KEY_FLAG_KEEP set should not be revoked. + * * If successful, 0 is returned. */ long keyctl_revoke_key(key_serial_t id) { key_ref_t key_ref; + struct key *key; long ret; key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE); @@ -377,8 +380,13 @@ long keyctl_revoke_key(key_serial_t id) } } - key_revoke(key_ref_to_ptr(key_ref)); - ret = 0; + key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + return -EPERM; + else { + key_revoke(key); + ret = 0; + } key_ref_put(key_ref); error: @@ -392,11 +400,14 @@ error: * The key and any links to the key will be automatically garbage collected * immediately. * + * Keys with KEY_FLAG_KEEP set should not be invalidated. + * * If successful, 0 is returned. */ long keyctl_invalidate_key(key_serial_t id) { key_ref_t key_ref; + struct key *key; long ret; kenter("%d", id); @@ -420,8 +431,13 @@ long keyctl_invalidate_key(key_serial_t id) } invalidate: - key_invalidate(key_ref_to_ptr(key_ref)); - ret = 0; + key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else { + key_invalidate(key); + ret = 0; + } error_put: key_ref_put(key_ref); error: @@ -433,12 +449,13 @@ error: * Clear the specified keyring, creating an empty process keyring if one of the * special keyring IDs is used. * - * The keyring must grant the caller Write permission for this to work. If - * successful, 0 will be returned. + * The keyring must grant the caller Write permission and not have + * KEY_FLAG_KEEP set for this to work. If successful, 0 will be returned. */ long keyctl_keyring_clear(key_serial_t ringid) { key_ref_t keyring_ref; + struct key *keyring; long ret; keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); @@ -460,7 +477,11 @@ long keyctl_keyring_clear(key_serial_t ringid) } clear: - ret = keyring_clear(key_ref_to_ptr(keyring_ref)); + keyring = key_ref_to_ptr(keyring_ref); + if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) + ret = -EPERM; + else + ret = keyring_clear(keyring); error_put: key_ref_put(keyring_ref); error: @@ -511,11 +532,14 @@ error: * itself need not grant the caller anything. If the last link to a key is * removed then that key will be scheduled for destruction. * + * Keys or keyrings with KEY_FLAG_KEEP set should not be unlinked. + * * If successful, 0 will be returned. */ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) { key_ref_t keyring_ref, key_ref; + struct key *keyring, *key; long ret; keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE); @@ -530,7 +554,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) goto error2; } - ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); + keyring = key_ref_to_ptr(keyring_ref); + key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_KEEP, &keyring->flags) && + test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + ret = key_unlink(keyring, key); key_ref_put(key_ref); error2: @@ -1289,6 +1319,8 @@ error: * the current time. The key and any links to the key will be automatically * garbage collected after the timeout expires. * + * Keys with KEY_FLAG_KEEP set should not be timed out. + * * If successful, 0 is returned. */ long keyctl_set_timeout(key_serial_t id, unsigned timeout) @@ -1320,10 +1352,14 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) okay: key = key_ref_to_ptr(key_ref); - key_set_timeout(key, timeout); + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else { + key_set_timeout(key, timeout); + ret = 0; + } key_put(key); - ret = 0; error: return ret; } -- cgit v1.2.3-55-g7522 From 501f1bde66525f94403a5b78832a9218ef9b1c14 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 10 Nov 2015 09:00:38 -0500 Subject: IMA: prevent keys on the .ima_blacklist from being removed Set the KEY_FLAGS_KEEP on the .ima_blacklist to prevent userspace from removing keys from the keyring. Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_mok.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'security') diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 18e37f57f512..8dad9a2b8e47 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -47,7 +47,9 @@ __init int ima_mok_init(void) if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) panic("Can't allocate IMA MOK or blacklist keyrings."); set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags); + set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags); + set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); return 0; } -- cgit v1.2.3-55-g7522 From 6ad6afa14610c1fed3303c719b1f8f86f19f1fd3 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 7 Dec 2015 14:35:47 -0500 Subject: ima: update appraise flags after policy update completes While creating a temporary list of new rules, the ima_appraise flag is updated, but not reverted on failure to append the new rules to the existing policy. This patch defines temp_ima_appraise flag. Only when the new rules are appended to the policy is the flag updated. Signed-off-by: Mimi Zohar Acked-by: Petko Manolov --- security/integrity/ima/ima_policy.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 2f4e0f5f31e2..ba5d2fc61394 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -40,6 +40,7 @@ #define AUDIT 0x0040 int ima_policy_flag; +static int temp_ima_appraise; #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, @@ -370,6 +371,7 @@ void ima_update_policy_flag(void) ima_policy_flag |= entry->action; } + ima_appraise |= temp_ima_appraise; if (!ima_appraise) ima_policy_flag &= ~IMA_APPRAISE; } @@ -757,9 +759,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) if (!result && (entry->action == UNKNOWN)) result = -EINVAL; else if (entry->func == MODULE_CHECK) - ima_appraise |= IMA_APPRAISE_MODULES; + temp_ima_appraise |= IMA_APPRAISE_MODULES; else if (entry->func == FIRMWARE_CHECK) - ima_appraise |= IMA_APPRAISE_FIRMWARE; + temp_ima_appraise |= IMA_APPRAISE_FIRMWARE; audit_log_format(ab, "res=%d", !result); audit_log_end(ab); return result; @@ -821,6 +823,7 @@ void ima_delete_rules(void) struct ima_rule_entry *entry, *tmp; int i; + temp_ima_appraise = 0; list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) { for (i = 0; i < MAX_LSM_RULES; i++) kfree(entry->lsm[i].args_p); -- cgit v1.2.3-55-g7522 From 92cc916638a48f285736cd5541536e2e1b73ecf8 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 9 Dec 2015 17:37:16 -0500 Subject: security/integrity: make ima/ima_mok.c explicitly non-modular The Kconfig currently controlling compilation of this code is: ima/Kconfig:config IMA_MOK_KEYRING ima/Kconfig: bool "Create IMA machine owner keys (MOK) and blacklist keyrings" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple of traces of modularity so that when reading the driver there is no doubt it really is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. Cc: Mimi Zohar Cc: Dmitry Kasatkin Cc: James Morris Cc: "Serge E. Hallyn" Cc: linux-ima-devel@lists.sourceforge.net Cc: linux-ima-user@lists.sourceforge.net Cc: linux-security-module@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_mok.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 8dad9a2b8e47..676885e4320e 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include @@ -52,5 +52,4 @@ __init int ima_mok_init(void) set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); return 0; } - -module_init(ima_mok_init); +device_initcall(ima_mok_init); -- cgit v1.2.3-55-g7522 From 81bd0d56298f93af6ac233d8a7e8b29aa4b094b7 Mon Sep 17 00:00:00 2001 From: Roman Kubiak Date: Thu, 17 Dec 2015 13:24:35 +0100 Subject: Smack: type confusion in smak sendmsg() handler Smack security handler for sendmsg() syscall is vulnerable to type confusion issue what can allow to privilege escalation into root or cause denial of service. A malicious attacker can create socket of one type for example AF_UNIX and pass is into sendmsg() function ensuring that this is AF_INET socket. Remedy Do not trust user supplied data. Proposed fix below. Signed-off-by: Roman Kubiak Signed-off-by: Mateusz Fruba Acked-by: Casey Schaufler --- security/smack/smack_lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index b20ef0602267..0e7703773a97 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3780,7 +3780,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, if (sip == NULL) return 0; - switch (sip->sin_family) { + switch (sock->sk->sk_family) { case AF_INET: rc = smack_netlabel_send(sock->sk, sip); break; -- cgit v1.2.3-55-g7522 From 5208cc83423dde06924121a85368c721a27ca555 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 12 Dec 2015 13:19:52 +0200 Subject: keys, trusted: fix: *do not* allow duplicate key options The trusted keys option parsing allows specifying the same option multiple times. The last option value specified is used. This is problematic because: * No gain. * This makes complicated to specify options that are dependent on other options. This patch changes the behavior in a way that option can be specified only once. Reported-by: James Morris James Morris Reviewed-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe --- security/keys/trusted.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'security') diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 903dace648a1..7c183c767a3a 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -736,11 +736,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay, int res; unsigned long handle; unsigned long lock; + unsigned long token_mask = 0; while ((p = strsep(&c, " \t"))) { if (*p == '\0' || *p == ' ' || *p == '\t') continue; token = match_token(p, key_tokens, args); + if (test_and_set_bit(token, &token_mask)) + return -EINVAL; switch (token) { case Opt_pcrinfo: -- cgit v1.2.3-55-g7522 From 5ca4c20cfd37bac6486de040e9951b3b34755238 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 5 Nov 2015 21:43:06 +0200 Subject: keys, trusted: select hash algorithm for TPM2 chips Added 'hash=' option for selecting the hash algorithm for add_key() syscall and documentation for it. Added entry for sm3-256 to the following tables in order to support TPM_ALG_SM3_256: * hash_algo_name * hash_digest_size Includes support for the following hash algorithms: * sha1 * sha256 * sha384 * sha512 * sm3-256 Signed-off-by: Jarkko Sakkinen Tested-by: Colin Ian King Reviewed-by: James Morris Reviewed-by: Mimi Zohar Acked-by: Peter Huewe --- Documentation/security/keys-trusted-encrypted.txt | 3 ++ crypto/hash_info.c | 2 ++ drivers/char/tpm/tpm.h | 10 +++++-- drivers/char/tpm/tpm2-cmd.c | 36 +++++++++++++++++++++-- include/crypto/hash_info.h | 3 ++ include/keys/trusted-type.h | 1 + include/uapi/linux/hash_info.h | 1 + security/keys/Kconfig | 1 + security/keys/trusted.c | 27 ++++++++++++++++- 9 files changed, 77 insertions(+), 7 deletions(-) (limited to 'security') diff --git a/Documentation/security/keys-trusted-encrypted.txt b/Documentation/security/keys-trusted-encrypted.txt index e105ae97a4f5..fd2565b301e8 100644 --- a/Documentation/security/keys-trusted-encrypted.txt +++ b/Documentation/security/keys-trusted-encrypted.txt @@ -38,6 +38,9 @@ Usage: pcrlock= pcr number to be extended to "lock" blob migratable= 0|1 indicating permission to reseal to new PCR values, default 1 (resealing allowed) + hash= hash algorithm name as a string. For TPM 1.x the only + allowed value is sha1. For TPM 2.x the allowed values + are sha1, sha256, sha384, sha512 and sm3-256. "keyctl print" returns an ascii hex copy of the sealed key, which is in standard TPM_STORED_DATA format. The key length for new keys are always in bytes. diff --git a/crypto/hash_info.c b/crypto/hash_info.c index 3e7ff46f26e8..7b1e0b188ce6 100644 --- a/crypto/hash_info.c +++ b/crypto/hash_info.c @@ -31,6 +31,7 @@ const char *const hash_algo_name[HASH_ALGO__LAST] = { [HASH_ALGO_TGR_128] = "tgr128", [HASH_ALGO_TGR_160] = "tgr160", [HASH_ALGO_TGR_192] = "tgr192", + [HASH_ALGO_SM3_256] = "sm3-256", }; EXPORT_SYMBOL_GPL(hash_algo_name); @@ -52,5 +53,6 @@ const int hash_digest_size[HASH_ALGO__LAST] = { [HASH_ALGO_TGR_128] = TGR128_DIGEST_SIZE, [HASH_ALGO_TGR_160] = TGR160_DIGEST_SIZE, [HASH_ALGO_TGR_192] = TGR192_DIGEST_SIZE, + [HASH_ALGO_SM3_256] = SM3256_DIGEST_SIZE, }; EXPORT_SYMBOL_GPL(hash_digest_size); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 347fc615bcc9..542a80cbfd9c 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -83,16 +83,20 @@ enum tpm2_structures { }; enum tpm2_return_codes { - TPM2_RC_INITIALIZE = 0x0100, - TPM2_RC_TESTING = 0x090A, + TPM2_RC_HASH = 0x0083, /* RC_FMT1 */ + TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ TPM2_RC_DISABLED = 0x0120, + TPM2_RC_TESTING = 0x090A, /* RC_WARN */ }; enum tpm2_algorithms { TPM2_ALG_SHA1 = 0x0004, TPM2_ALG_KEYEDHASH = 0x0008, TPM2_ALG_SHA256 = 0x000B, - TPM2_ALG_NULL = 0x0010 + TPM2_ALG_SHA384 = 0x000C, + TPM2_ALG_SHA512 = 0x000D, + TPM2_ALG_NULL = 0x0010, + TPM2_ALG_SM3_256 = 0x0012, }; enum tpm2_command_codes { diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index c12130485fc1..d9d082206f6e 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -16,6 +16,7 @@ */ #include "tpm.h" +#include #include enum tpm2_object_attributes { @@ -104,6 +105,19 @@ struct tpm2_cmd { union tpm2_cmd_params params; } __packed; +struct tpm2_hash { + unsigned int crypto_id; + unsigned int tpm_id; +}; + +static struct tpm2_hash tpm2_hash_map[] = { + {HASH_ALGO_SHA1, TPM2_ALG_SHA1}, + {HASH_ALGO_SHA256, TPM2_ALG_SHA256}, + {HASH_ALGO_SHA384, TPM2_ALG_SHA384}, + {HASH_ALGO_SHA512, TPM2_ALG_SHA512}, + {HASH_ALGO_SM3_256, TPM2_ALG_SM3_256}, +}; + /* * Array with one entry per ordinal defining the maximum amount * of time the chip could take to return the result. The values @@ -429,8 +443,20 @@ int tpm2_seal_trusted(struct tpm_chip *chip, { unsigned int blob_len; struct tpm_buf buf; + u32 hash; + int i; int rc; + for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { + if (options->hash == tpm2_hash_map[i].crypto_id) { + hash = tpm2_hash_map[i].tpm_id; + break; + } + } + + if (i == ARRAY_SIZE(tpm2_hash_map)) + return -EINVAL; + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); if (rc) return rc; @@ -455,7 +481,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, tpm_buf_append_u16(&buf, 14); tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); - tpm_buf_append_u16(&buf, TPM2_ALG_SHA256); + tpm_buf_append_u16(&buf, hash); tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); tpm_buf_append_u16(&buf, 0); /* policy digest size */ tpm_buf_append_u16(&buf, TPM2_ALG_NULL); @@ -488,8 +514,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip, out: tpm_buf_destroy(&buf); - if (rc > 0) - rc = -EPERM; + if (rc > 0) { + if ((rc & TPM2_RC_HASH) == TPM2_RC_HASH) + rc = -EINVAL; + else + rc = -EPERM; + } return rc; } diff --git a/include/crypto/hash_info.h b/include/crypto/hash_info.h index e1e5a3e5dd1b..56f217d41f12 100644 --- a/include/crypto/hash_info.h +++ b/include/crypto/hash_info.h @@ -34,6 +34,9 @@ #define TGR160_DIGEST_SIZE 20 #define TGR192_DIGEST_SIZE 24 +/* not defined in include/crypto/ */ +#define SM3256_DIGEST_SIZE 32 + extern const char *const hash_algo_name[HASH_ALGO__LAST]; extern const int hash_digest_size[HASH_ALGO__LAST]; diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index f91ecd9d1bb1..a6a100833ae9 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -36,6 +36,7 @@ struct trusted_key_options { uint32_t pcrinfo_len; unsigned char pcrinfo[MAX_PCRINFO_SIZE]; int pcrlock; + uint32_t hash; }; extern struct key_type key_type_trusted; diff --git a/include/uapi/linux/hash_info.h b/include/uapi/linux/hash_info.h index ca18c45f8304..ebf8fd885dd5 100644 --- a/include/uapi/linux/hash_info.h +++ b/include/uapi/linux/hash_info.h @@ -31,6 +31,7 @@ enum hash_algo { HASH_ALGO_TGR_128, HASH_ALGO_TGR_160, HASH_ALGO_TGR_192, + HASH_ALGO_SM3_256, HASH_ALGO__LAST }; diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 72483b8f1be5..fe4d74e126a7 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -54,6 +54,7 @@ config TRUSTED_KEYS select CRYPTO select CRYPTO_HMAC select CRYPTO_SHA1 + select CRYPTO_HASH_INFO 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.c b/security/keys/trusted.c index 7c183c767a3a..8f1300cab38e 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -11,6 +11,7 @@ * See Documentation/security/keys-trusted-encrypted.txt */ +#include #include #include #include @@ -710,7 +711,8 @@ enum { Opt_err = -1, Opt_new, Opt_load, Opt_update, Opt_keyhandle, Opt_keyauth, Opt_blobauth, - Opt_pcrinfo, Opt_pcrlock, Opt_migratable + Opt_pcrinfo, Opt_pcrlock, Opt_migratable, + Opt_hash, }; static const match_table_t key_tokens = { @@ -723,6 +725,7 @@ static const match_table_t key_tokens = { {Opt_pcrinfo, "pcrinfo=%s"}, {Opt_pcrlock, "pcrlock=%s"}, {Opt_migratable, "migratable=%s"}, + {Opt_hash, "hash=%s"}, {Opt_err, NULL} }; @@ -737,6 +740,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay, unsigned long handle; unsigned long lock; unsigned long token_mask = 0; + int i; + int tpm2; + + tpm2 = tpm_is_tpm2(TPM_ANY_NUM); + if (tpm2 < 0) + return tpm2; + + opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1; while ((p = strsep(&c, " \t"))) { if (*p == '\0' || *p == ' ' || *p == '\t') @@ -790,6 +801,20 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; opt->pcrlock = lock; break; + case Opt_hash: + for (i = 0; i < HASH_ALGO__LAST; i++) { + if (!strcmp(args[0].from, hash_algo_name[i])) { + opt->hash = i; + break; + } + } + 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"); + return -EINVAL; + } + break; default: return -EINVAL; } -- cgit v1.2.3-55-g7522 From 5beb0c435bdde35a09376566b0e28f7df87c9f68 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 31 Oct 2015 17:53:44 +0200 Subject: keys, trusted: seal with a TPM2 authorization policy TPM2 supports authorization policies, which are essentially combinational logic statements repsenting the conditions where the data can be unsealed based on the TPM state. This patch enables to use authorization policies to seal trusted keys. Two following new options have been added for trusted keys: * 'policydigest=': provide an auth policy digest for sealing. * 'policyhandle=': provide a policy session handle for unsealing. If 'hash=' option is supplied after 'policydigest=' option, this will result an error because the state of the option would become mixed. Signed-off-by: Jarkko Sakkinen Tested-by: Colin Ian King Reviewed-by: Mimi Zohar Acked-by: Peter Huewe --- Documentation/security/keys-trusted-encrypted.txt | 34 +++++++++++++---------- drivers/char/tpm/tpm2-cmd.c | 24 +++++++++++++--- include/keys/trusted-type.h | 4 +++ security/keys/trusted.c | 26 +++++++++++++++++ 4 files changed, 70 insertions(+), 18 deletions(-) (limited to 'security') diff --git a/Documentation/security/keys-trusted-encrypted.txt b/Documentation/security/keys-trusted-encrypted.txt index fd2565b301e8..324ddf5223b3 100644 --- a/Documentation/security/keys-trusted-encrypted.txt +++ b/Documentation/security/keys-trusted-encrypted.txt @@ -27,20 +27,26 @@ Usage: keyctl print keyid options: - keyhandle= ascii hex value of sealing key default 0x40000000 (SRK) - keyauth= ascii hex auth for sealing key default 0x00...i - (40 ascii zeros) - blobauth= ascii hex auth for sealed data default 0x00... - (40 ascii zeros) - blobauth= ascii hex auth for sealed data default 0x00... - (40 ascii zeros) - pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no default) - pcrlock= pcr number to be extended to "lock" blob - migratable= 0|1 indicating permission to reseal to new PCR values, - default 1 (resealing allowed) - hash= hash algorithm name as a string. For TPM 1.x the only - allowed value is sha1. For TPM 2.x the allowed values - are sha1, sha256, sha384, sha512 and sm3-256. + keyhandle= ascii hex value of sealing key default 0x40000000 (SRK) + keyauth= ascii hex auth for sealing key default 0x00...i + (40 ascii zeros) + blobauth= ascii hex auth for sealed data default 0x00... + (40 ascii zeros) + blobauth= ascii hex auth for sealed data default 0x00... + (40 ascii zeros) + pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no default) + pcrlock= pcr number to be extended to "lock" blob + migratable= 0|1 indicating permission to reseal to new PCR values, + default 1 (resealing allowed) + hash= hash algorithm name as a string. For TPM 1.x the only + allowed value is sha1. For TPM 2.x the allowed values + are sha1, sha256, sha384, sha512 and sm3-256. + policydigest= digest for the authorization policy. must be calculated + with the same hash algorithm as specified by the 'hash=' + option. + policyhandle= handle to an authorization policy session that defines the + same policy and with the same hash algorithm as was used to + seal the key. "keyctl print" returns an ascii hex copy of the sealed key, which is in standard TPM_STORED_DATA format. The key length for new keys are always in bytes. diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index d9d082206f6e..45a634016f95 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -478,12 +478,26 @@ int tpm2_seal_trusted(struct tpm_chip *chip, tpm_buf_append_u8(&buf, payload->migratable); /* public */ - tpm_buf_append_u16(&buf, 14); + if (options->policydigest) + tpm_buf_append_u16(&buf, 14 + options->digest_len); + else + tpm_buf_append_u16(&buf, 14); tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); tpm_buf_append_u16(&buf, hash); - tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); - tpm_buf_append_u16(&buf, 0); /* policy digest size */ + + /* policy */ + if (options->policydigest) { + tpm_buf_append_u32(&buf, 0); + tpm_buf_append_u16(&buf, options->digest_len); + tpm_buf_append(&buf, options->policydigest, + options->digest_len); + } else { + tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); + tpm_buf_append_u16(&buf, 0); + } + + /* public parameters */ tpm_buf_append_u16(&buf, TPM2_ALG_NULL); tpm_buf_append_u16(&buf, 0); @@ -613,7 +627,9 @@ static int tpm2_unseal(struct tpm_chip *chip, return rc; tpm_buf_append_u32(&buf, blob_handle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, + tpm2_buf_append_auth(&buf, + options->policyhandle ? + options->policyhandle : TPM2_RS_PW, NULL /* nonce */, 0, 0 /* session_attributes */, options->blobauth /* hmac */, diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index a6a100833ae9..42cf2d991bf4 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -18,6 +18,7 @@ #define MAX_KEY_SIZE 128 #define MAX_BLOB_SIZE 512 #define MAX_PCRINFO_SIZE 64 +#define MAX_DIGEST_SIZE 64 struct trusted_key_payload { struct rcu_head rcu; @@ -37,6 +38,9 @@ struct trusted_key_options { unsigned char pcrinfo[MAX_PCRINFO_SIZE]; int pcrlock; uint32_t hash; + uint32_t digest_len; + unsigned char policydigest[MAX_DIGEST_SIZE]; + uint32_t policyhandle; }; extern struct key_type key_type_trusted; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 8f1300cab38e..e15baf722ae3 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -713,6 +713,8 @@ enum { Opt_keyhandle, Opt_keyauth, Opt_blobauth, Opt_pcrinfo, Opt_pcrlock, Opt_migratable, Opt_hash, + Opt_policydigest, + Opt_policyhandle, }; static const match_table_t key_tokens = { @@ -726,6 +728,8 @@ static const match_table_t key_tokens = { {Opt_pcrlock, "pcrlock=%s"}, {Opt_migratable, "migratable=%s"}, {Opt_hash, "hash=%s"}, + {Opt_policydigest, "policydigest=%s"}, + {Opt_policyhandle, "policyhandle=%s"}, {Opt_err, NULL} }; @@ -748,6 +752,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return tpm2; opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1; + opt->digest_len = hash_digest_size[opt->hash]; while ((p = strsep(&c, " \t"))) { if (*p == '\0' || *p == ' ' || *p == '\t') @@ -802,9 +807,13 @@ static int getoptions(char *c, struct trusted_key_payload *pay, opt->pcrlock = lock; break; case Opt_hash: + if (test_bit(Opt_policydigest, &token_mask)) + return -EINVAL; for (i = 0; i < HASH_ALGO__LAST; i++) { if (!strcmp(args[0].from, hash_algo_name[i])) { opt->hash = i; + opt->digest_len = + hash_digest_size[opt->hash]; break; } } @@ -815,6 +824,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; } break; + case Opt_policydigest: + if (!tpm2 || + strlen(args[0].from) != (2 * opt->digest_len)) + return -EINVAL; + res = hex2bin(opt->policydigest, args[0].from, + opt->digest_len); + if (res < 0) + return -EINVAL; + break; + case Opt_policyhandle: + if (!tpm2) + return -EINVAL; + res = kstrtoul(args[0].from, 16, &handle); + if (res < 0) + return -EINVAL; + opt->policyhandle = handle; + break; default: return -EINVAL; } -- cgit v1.2.3-55-g7522 From a44ca52ca6bd10ce588102789dde33f9fd6569cf Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:39 -0500 Subject: selinux: Remove unused variable in selinux_inode_init_security Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d0cfaa9f19d0..714acadc027e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2753,13 +2753,11 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, void **value, size_t *len) { const struct task_security_struct *tsec = current_security(); - struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; u32 sid, newsid, clen; int rc; char *context; - dsec = dir->i_security; sbsec = dir->i_sb->s_security; sid = tsec->sid; -- cgit v1.2.3-55-g7522 From ea861dfd9e0e7e044a6e65fa02a14b9159b568da Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:39 -0500 Subject: security: Make inode argument of inode_getsecurity non-const Make the inode argument of the inode_getsecurity hook non-const so that we can use it to revalidate invalid security labels. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- include/linux/lsm_hooks.h | 2 +- include/linux/security.h | 4 ++-- security/security.c | 2 +- security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index ec3a6bab29de..bdd0a3a8a0e4 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1413,7 +1413,7 @@ union security_list_options { int (*inode_removexattr)(struct dentry *dentry, const char *name); int (*inode_need_killpriv)(struct dentry *dentry); int (*inode_killpriv)(struct dentry *dentry); - int (*inode_getsecurity)(const struct inode *inode, const char *name, + int (*inode_getsecurity)(struct inode *inode, const char *name, void **buffer, bool alloc); int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, diff --git a/include/linux/security.h b/include/linux/security.h index 2f4c1f7aa7db..9ee61b264b23 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -270,7 +270,7 @@ int security_inode_listxattr(struct dentry *dentry); int security_inode_removexattr(struct dentry *dentry, const char *name); int security_inode_need_killpriv(struct dentry *dentry); int security_inode_killpriv(struct dentry *dentry); -int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc); +int security_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc); int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags); int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size); void security_inode_getsecid(const struct inode *inode, u32 *secid); @@ -719,7 +719,7 @@ static inline int security_inode_killpriv(struct dentry *dentry) return cap_inode_killpriv(dentry); } -static inline int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc) +static inline int security_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc) { return -EOPNOTSUPP; } diff --git a/security/security.c b/security/security.c index 46f405ce6b0f..73514c91d87f 100644 --- a/security/security.c +++ b/security/security.c @@ -697,7 +697,7 @@ int security_inode_killpriv(struct dentry *dentry) return call_int_hook(inode_killpriv, 0, dentry); } -int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc) +int security_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc) { if (unlikely(IS_PRIVATE(inode))) return -EOPNOTSUPP; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 714acadc027e..2e40c9c4e12c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3108,7 +3108,7 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name) * * Permission check is handled by selinux_inode_getxattr hook. */ -static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc) +static int selinux_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc) { u32 size; int error; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ff81026f6ddb..f0e694bccfd4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1465,7 +1465,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) * * Returns the size of the attribute or an error code */ -static int smack_inode_getsecurity(const struct inode *inode, +static int smack_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc) { -- cgit v1.2.3-55-g7522 From d6335d77a7622a88380f3f207cc1f727f878dd21 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:39 -0500 Subject: security: Make inode argument of inode_getsecid non-const Make the inode argument of the inode_getsecid hook non-const so that we can use it to revalidate invalid security labels. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- include/linux/audit.h | 8 ++++---- include/linux/lsm_hooks.h | 2 +- include/linux/security.h | 4 ++-- kernel/audit.c | 2 +- kernel/audit.h | 2 +- kernel/auditsc.c | 6 +++--- security/security.c | 2 +- security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) (limited to 'security') diff --git a/include/linux/audit.h b/include/linux/audit.h index 20eba1eb0a3c..8a2d046e9f6b 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -137,7 +137,7 @@ extern void __audit_getname(struct filename *name); extern void __audit_inode(struct filename *name, const struct dentry *dentry, unsigned int flags); extern void __audit_file(const struct file *); -extern void __audit_inode_child(const struct inode *parent, +extern void __audit_inode_child(struct inode *parent, const struct dentry *dentry, const unsigned char type); extern void __audit_seccomp(unsigned long syscall, long signr, int code); @@ -202,7 +202,7 @@ static inline void audit_inode_parent_hidden(struct filename *name, __audit_inode(name, dentry, AUDIT_INODE_PARENT | AUDIT_INODE_HIDDEN); } -static inline void audit_inode_child(const struct inode *parent, +static inline void audit_inode_child(struct inode *parent, const struct dentry *dentry, const unsigned char type) { if (unlikely(!audit_dummy_context())) @@ -359,7 +359,7 @@ static inline void __audit_inode(struct filename *name, const struct dentry *dentry, unsigned int flags) { } -static inline void __audit_inode_child(const struct inode *parent, +static inline void __audit_inode_child(struct inode *parent, const struct dentry *dentry, const unsigned char type) { } @@ -373,7 +373,7 @@ static inline void audit_file(struct file *file) static inline void audit_inode_parent_hidden(struct filename *name, const struct dentry *dentry) { } -static inline void audit_inode_child(const struct inode *parent, +static inline void audit_inode_child(struct inode *parent, const struct dentry *dentry, const unsigned char type) { } diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index bdd0a3a8a0e4..4c48227450e6 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1420,7 +1420,7 @@ union security_list_options { int flags); int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size); - void (*inode_getsecid)(const struct inode *inode, u32 *secid); + void (*inode_getsecid)(struct inode *inode, u32 *secid); int (*file_permission)(struct file *file, int mask); int (*file_alloc_security)(struct file *file); diff --git a/include/linux/security.h b/include/linux/security.h index 9ee61b264b23..e79149a06454 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -273,7 +273,7 @@ int security_inode_killpriv(struct dentry *dentry); int security_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc); int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags); int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size); -void security_inode_getsecid(const struct inode *inode, u32 *secid); +void security_inode_getsecid(struct inode *inode, u32 *secid); int security_file_permission(struct file *file, int mask); int security_file_alloc(struct file *file); void security_file_free(struct file *file); @@ -734,7 +734,7 @@ static inline int security_inode_listsecurity(struct inode *inode, char *buffer, return 0; } -static inline void security_inode_getsecid(const struct inode *inode, u32 *secid) +static inline void security_inode_getsecid(struct inode *inode, u32 *secid) { *secid = 0; } diff --git a/kernel/audit.c b/kernel/audit.c index 5ffcbd354a52..bc2ff61bc1d6 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1722,7 +1722,7 @@ static inline int audit_copy_fcaps(struct audit_names *name, /* Copy inode data into an audit_names. */ void audit_copy_inode(struct audit_names *name, const struct dentry *dentry, - const struct inode *inode) + struct inode *inode) { name->ino = inode->i_ino; name->dev = inode->i_sb->s_dev; diff --git a/kernel/audit.h b/kernel/audit.h index de6cbb7cf547..cbbe6bb6496e 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -207,7 +207,7 @@ extern u32 audit_ever_enabled; extern void audit_copy_inode(struct audit_names *name, const struct dentry *dentry, - const struct inode *inode); + struct inode *inode); extern void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap); extern void audit_log_name(struct audit_context *context, diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b86cc04959de..195ffaee50b9 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1754,7 +1754,7 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, unsigned int flags) { struct audit_context *context = current->audit_context; - const struct inode *inode = d_backing_inode(dentry); + struct inode *inode = d_backing_inode(dentry); struct audit_names *n; bool parent = flags & AUDIT_INODE_PARENT; @@ -1848,12 +1848,12 @@ void __audit_file(const struct file *file) * must be hooked prior, in order to capture the target inode during * unsuccessful attempts. */ -void __audit_inode_child(const struct inode *parent, +void __audit_inode_child(struct inode *parent, const struct dentry *dentry, const unsigned char type) { struct audit_context *context = current->audit_context; - const struct inode *inode = d_backing_inode(dentry); + struct inode *inode = d_backing_inode(dentry); const char *dname = dentry->d_name.name; struct audit_names *n, *found_parent = NULL, *found_child = NULL; diff --git a/security/security.c b/security/security.c index 73514c91d87f..c5beb7e90721 100644 --- a/security/security.c +++ b/security/security.c @@ -721,7 +721,7 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer } EXPORT_SYMBOL(security_inode_listsecurity); -void security_inode_getsecid(const struct inode *inode, u32 *secid) +void security_inode_getsecid(struct inode *inode, u32 *secid) { call_void_hook(inode_getsecid, inode, secid); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2e40c9c4e12c..19a8f1500a7e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3180,7 +3180,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t return len; } -static void selinux_inode_getsecid(const struct inode *inode, u32 *secid) +static void selinux_inode_getsecid(struct inode *inode, u32 *secid) { struct inode_security_struct *isec = inode->i_security; *secid = isec->sid; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index f0e694bccfd4..ac7436f1bc2b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1538,7 +1538,7 @@ static int smack_inode_listsecurity(struct inode *inode, char *buffer, * @inode: inode to extract the info from * @secid: where result will be saved */ -static void smack_inode_getsecid(const struct inode *inode, u32 *secid) +static void smack_inode_getsecid(struct inode *inode, u32 *secid) { struct inode_smack *isp = inode->i_security; -- cgit v1.2.3-55-g7522 From 83da53c5a34564a0a63b26f84293c6e2a639e1e4 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:39 -0500 Subject: selinux: Add accessor functions for inode->i_security Add functions dentry_security and inode_security for accessing inode->i_security. These functions initially don't do much, but they will later be used to revalidate the security labels when necessary. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 97 ++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 41 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 19a8f1500a7e..1dc0d79a6a25 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -242,6 +242,24 @@ static int inode_alloc_security(struct inode *inode) return 0; } +/* + * Get the security label of an inode. + */ +static struct inode_security_struct *inode_security(struct inode *inode) +{ + return inode->i_security; +} + +/* + * Get the security label of a dentry's backing inode. + */ +static struct inode_security_struct *backing_inode_security(struct dentry *dentry) +{ + struct inode *inode = d_backing_inode(dentry); + + return inode->i_security; +} + static void inode_free_rcu(struct rcu_head *head) { struct inode_security_struct *isec; @@ -565,8 +583,8 @@ static int selinux_get_mnt_opts(const struct super_block *sb, opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT; } if (sbsec->flags & ROOTCONTEXT_MNT) { - struct inode *root = d_backing_inode(sbsec->sb->s_root); - struct inode_security_struct *isec = root->i_security; + struct dentry *root = sbsec->sb->s_root; + struct inode_security_struct *isec = backing_inode_security(root); rc = security_sid_to_context(isec->sid, &context, &len); if (rc) @@ -621,8 +639,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, int rc = 0, i; struct superblock_security_struct *sbsec = sb->s_security; const char *name = sb->s_type->name; - struct inode *inode = d_backing_inode(sbsec->sb->s_root); - struct inode_security_struct *root_isec = inode->i_security; + struct dentry *root = sbsec->sb->s_root; + struct inode_security_struct *root_isec = backing_inode_security(root); u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; u32 defcontext_sid = 0; char **mount_options = opts->mnt_opts; @@ -852,8 +870,8 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb, if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid) goto mismatch; if (oldflags & ROOTCONTEXT_MNT) { - struct inode_security_struct *oldroot = d_backing_inode(oldsb->s_root)->i_security; - struct inode_security_struct *newroot = d_backing_inode(newsb->s_root)->i_security; + struct inode_security_struct *oldroot = backing_inode_security(oldsb->s_root); + struct inode_security_struct *newroot = backing_inode_security(newsb->s_root); if (oldroot->sid != newroot->sid) goto mismatch; } @@ -903,17 +921,14 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, if (!set_fscontext) newsbsec->sid = sid; if (!set_rootcontext) { - struct inode *newinode = d_backing_inode(newsb->s_root); - struct inode_security_struct *newisec = newinode->i_security; + struct inode_security_struct *newisec = backing_inode_security(newsb->s_root); newisec->sid = sid; } newsbsec->mntpoint_sid = sid; } if (set_rootcontext) { - const struct inode *oldinode = d_backing_inode(oldsb->s_root); - const struct inode_security_struct *oldisec = oldinode->i_security; - struct inode *newinode = d_backing_inode(newsb->s_root); - struct inode_security_struct *newisec = newinode->i_security; + const struct inode_security_struct *oldisec = backing_inode_security(oldsb->s_root); + struct inode_security_struct *newisec = backing_inode_security(newsb->s_root); newisec->sid = oldisec->sid; } @@ -1712,13 +1727,13 @@ out: /* * Determine the label for an inode that might be unioned. */ -static int selinux_determine_inode_label(const struct inode *dir, +static int selinux_determine_inode_label(struct inode *dir, const struct qstr *name, u16 tclass, u32 *_new_isid) { const struct superblock_security_struct *sbsec = dir->i_sb->s_security; - const struct inode_security_struct *dsec = dir->i_security; + const struct inode_security_struct *dsec = inode_security(dir); const struct task_security_struct *tsec = current_security(); if ((sbsec->flags & SE_SBINITIALIZED) && @@ -1747,7 +1762,7 @@ static int may_create(struct inode *dir, struct common_audit_data ad; int rc; - dsec = dir->i_security; + dsec = inode_security(dir); sbsec = dir->i_sb->s_security; sid = tsec->sid; @@ -1800,8 +1815,8 @@ static int may_link(struct inode *dir, u32 av; int rc; - dsec = dir->i_security; - isec = d_backing_inode(dentry)->i_security; + dsec = inode_security(dir); + isec = backing_inode_security(dentry); ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; @@ -1844,10 +1859,10 @@ static inline int may_rename(struct inode *old_dir, int old_is_dir, new_is_dir; int rc; - old_dsec = old_dir->i_security; - old_isec = d_backing_inode(old_dentry)->i_security; + old_dsec = inode_security(old_dir); + old_isec = backing_inode_security(old_dentry); old_is_dir = d_is_dir(old_dentry); - new_dsec = new_dir->i_security; + new_dsec = inode_security(new_dir); ad.type = LSM_AUDIT_DATA_DENTRY; @@ -1875,7 +1890,7 @@ static inline int may_rename(struct inode *old_dir, if (rc) return rc; if (d_is_positive(new_dentry)) { - new_isec = d_backing_inode(new_dentry)->i_security; + new_isec = backing_inode_security(new_dentry); new_is_dir = d_is_dir(new_dentry); rc = avc_has_perm(sid, new_isec->sid, new_isec->sclass, @@ -2011,8 +2026,8 @@ static int selinux_binder_transfer_file(struct task_struct *from, { u32 sid = task_sid(to); struct file_security_struct *fsec = file->f_security; - struct inode *inode = d_backing_inode(file->f_path.dentry); - struct inode_security_struct *isec = inode->i_security; + struct dentry *dentry = file->f_path.dentry; + struct inode_security_struct *isec = backing_inode_security(dentry); struct common_audit_data ad; int rc; @@ -2028,7 +2043,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, return rc; } - if (unlikely(IS_PRIVATE(inode))) + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), @@ -2217,7 +2232,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) old_tsec = current_security(); new_tsec = bprm->cred->security; - isec = inode->i_security; + isec = inode_security(inode); /* Default to the current task SID. */ new_tsec->sid = old_tsec->sid; @@ -2639,7 +2654,7 @@ static int selinux_sb_remount(struct super_block *sb, void *data) break; case ROOTCONTEXT_MNT: { struct inode_security_struct *root_isec; - root_isec = d_backing_inode(sb->s_root)->i_security; + root_isec = backing_inode_security(sb->s_root); if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid)) goto out_bad_option; @@ -2856,7 +2871,7 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; sid = cred_sid(cred); - isec = inode->i_security; + isec = inode_security(inode); return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad, rcu ? MAY_NOT_BLOCK : 0); @@ -2908,7 +2923,7 @@ static int selinux_inode_permission(struct inode *inode, int mask) perms = file_mask_to_av(inode->i_mode, mask); sid = cred_sid(cred); - isec = inode->i_security; + isec = inode_security(inode); rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); audited = avc_audit_required(perms, &avd, rc, @@ -2978,7 +2993,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct inode *inode = d_backing_inode(dentry); - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = backing_inode_security(dentry); struct superblock_security_struct *sbsec; struct common_audit_data ad; u32 newsid, sid = current_sid(); @@ -3055,7 +3070,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, int flags) { struct inode *inode = d_backing_inode(dentry); - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = backing_inode_security(dentry); u32 newsid; int rc; @@ -3113,7 +3128,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void u32 size; int error; char *context = NULL; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); if (strcmp(name, XATTR_SELINUX_SUFFIX)) return -EOPNOTSUPP; @@ -3152,7 +3167,7 @@ out_nofree: static int selinux_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); u32 newsid; int rc; @@ -3182,7 +3197,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t static void selinux_inode_getsecid(struct inode *inode, u32 *secid) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); *secid = isec->sid; } @@ -3205,7 +3220,7 @@ static int selinux_file_permission(struct file *file, int mask) { struct inode *inode = file_inode(file); struct file_security_struct *fsec = file->f_security; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); u32 sid = current_sid(); if (!mask) @@ -3240,7 +3255,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, struct common_audit_data ad; struct file_security_struct *fsec = file->f_security; struct inode *inode = file_inode(file); - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); struct lsm_ioctlop_audit ioctl; u32 ssid = cred_sid(cred); int rc; @@ -3504,7 +3519,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) struct inode_security_struct *isec; fsec = file->f_security; - isec = file_inode(file)->i_security; + isec = inode_security(file_inode(file)); /* * Save inode label and policy sequence number * at open-time so that selinux_file_permission @@ -3622,7 +3637,7 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid) */ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = inode_security(inode); struct task_security_struct *tsec = new->security; u32 sid = current_sid(); int ret; @@ -4063,7 +4078,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern) { const struct task_security_struct *tsec = current_security(); - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; + struct inode_security_struct *isec = inode_security(SOCK_INODE(sock)); struct sk_security_struct *sksec; int err = 0; @@ -4263,9 +4278,9 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) if (err) return err; - newisec = SOCK_INODE(newsock)->i_security; + newisec = inode_security(SOCK_INODE(newsock)); - isec = SOCK_INODE(sock)->i_security; + isec = inode_security(SOCK_INODE(sock)); newisec->sclass = isec->sclass; newisec->sid = isec->sid; newisec->initialized = 1; @@ -4603,7 +4618,7 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid) static void selinux_sock_graft(struct sock *sk, struct socket *parent) { - struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; + struct inode_security_struct *isec = inode_security(SOCK_INODE(parent)); struct sk_security_struct *sksec = sk->sk_security; if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || -- cgit v1.2.3-55-g7522 From 6f3be9f562e3027c77bc4482ccf2cea8600a7f74 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:40 -0500 Subject: security: Add hook to invalidate inode security labels Add a hook to invalidate an inode's security label when the cached information becomes invalid. Add the new hook in selinux: set a flag when a security label becomes invalid. Signed-off-by: Andreas Gruenbacher Reviewed-by: James Morris Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- include/linux/lsm_hooks.h | 6 ++++++ include/linux/security.h | 5 +++++ security/security.c | 8 ++++++++ security/selinux/hooks.c | 30 ++++++++++++++++++++---------- security/selinux/include/objsec.h | 6 ++++++ 5 files changed, 45 insertions(+), 10 deletions(-) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 4c48227450e6..71969de4058c 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1261,6 +1261,10 @@ * audit_rule_init. * @rule contains the allocated rule * + * @inode_invalidate_secctx: + * Notify the security module that it must revalidate the security context + * of an inode. + * * @inode_notifysecctx: * Notify the security module of what the security context of an inode * should be. Initializes the incore security context managed by the @@ -1516,6 +1520,7 @@ union security_list_options { int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid); void (*release_secctx)(char *secdata, u32 seclen); + void (*inode_invalidate_secctx)(struct inode *inode); int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen); int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen); int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen); @@ -1757,6 +1762,7 @@ struct security_hook_heads { struct list_head secid_to_secctx; struct list_head secctx_to_secid; struct list_head release_secctx; + struct list_head inode_invalidate_secctx; struct list_head inode_notifysecctx; struct list_head inode_setsecctx; struct list_head inode_getsecctx; diff --git a/include/linux/security.h b/include/linux/security.h index e79149a06454..4824a4ccaf1c 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -353,6 +353,7 @@ int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); void security_release_secctx(char *secdata, u32 seclen); +void security_inode_invalidate_secctx(struct inode *inode); int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen); int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen); int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen); @@ -1093,6 +1094,10 @@ static inline void security_release_secctx(char *secdata, u32 seclen) { } +static inline void security_inode_invalidate_secctx(struct inode *inode) +{ +} + static inline int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) { return -EOPNOTSUPP; diff --git a/security/security.c b/security/security.c index c5beb7e90721..e8ffd92ae2eb 100644 --- a/security/security.c +++ b/security/security.c @@ -1161,6 +1161,12 @@ void security_release_secctx(char *secdata, u32 seclen) } EXPORT_SYMBOL(security_release_secctx); +void security_inode_invalidate_secctx(struct inode *inode) +{ + call_void_hook(inode_invalidate_secctx, inode); +} +EXPORT_SYMBOL(security_inode_invalidate_secctx); + int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) { return call_int_hook(inode_notifysecctx, 0, inode, ctx, ctxlen); @@ -1763,6 +1769,8 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.secctx_to_secid), .release_secctx = LIST_HEAD_INIT(security_hook_heads.release_secctx), + .inode_invalidate_secctx = + LIST_HEAD_INIT(security_hook_heads.inode_invalidate_secctx), .inode_notifysecctx = LIST_HEAD_INIT(security_hook_heads.inode_notifysecctx), .inode_setsecctx = diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1dc0d79a6a25..ef3bd9d61c38 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -820,7 +820,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, goto out; root_isec->sid = rootcontext_sid; - root_isec->initialized = 1; + root_isec->initialized = LABEL_INITIALIZED; } if (defcontext_sid) { @@ -1308,11 +1308,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent unsigned len = 0; int rc = 0; - if (isec->initialized) + if (isec->initialized == LABEL_INITIALIZED) goto out; mutex_lock(&isec->lock); - if (isec->initialized) + if (isec->initialized == LABEL_INITIALIZED) goto out_unlock; sbsec = inode->i_sb->s_security; @@ -1484,7 +1484,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent break; } - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; out_unlock: mutex_unlock(&isec->lock); @@ -2790,7 +2790,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, struct inode_security_struct *isec = inode->i_security; isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; } if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT)) @@ -3089,7 +3089,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; return; } @@ -3183,7 +3183,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; return 0; } @@ -3761,7 +3761,7 @@ static void selinux_task_to_inode(struct task_struct *p, u32 sid = task_sid(p); isec->sid = sid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; } /* Returns error only if unable to parse addresses */ @@ -4092,7 +4092,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, return err; } - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; if (sock->sk) { sksec = sock->sk->sk_security; @@ -4283,7 +4283,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) isec = inode_security(SOCK_INODE(sock)); newisec->sclass = isec->sclass; newisec->sid = isec->sid; - newisec->initialized = 1; + newisec->initialized = LABEL_INITIALIZED; return 0; } @@ -5775,6 +5775,15 @@ static void selinux_release_secctx(char *secdata, u32 seclen) kfree(secdata); } +static void selinux_inode_invalidate_secctx(struct inode *inode) +{ + struct inode_security_struct *isec = inode->i_security; + + mutex_lock(&isec->lock); + isec->initialized = LABEL_INVALID; + mutex_unlock(&isec->lock); +} + /* * called with inode->i_mutex locked */ @@ -6006,6 +6015,7 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx), LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid), LSM_HOOK_INIT(release_secctx, selinux_release_secctx), + LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx), LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx), LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx), LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx), diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 81fa718d5cb3..a2ae05414ba1 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -37,6 +37,12 @@ struct task_security_struct { u32 sockcreate_sid; /* fscreate SID */ }; +enum label_initialized { + LABEL_MISSING, /* not initialized */ + LABEL_INITIALIZED, /* inizialized */ + LABEL_INVALID /* invalid */ +}; + struct inode_security_struct { struct inode *inode; /* back pointer to inode object */ union { -- cgit v1.2.3-55-g7522 From 5d226df4edfa0eb1e689e7ac2741cf261ff7cbf1 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Dec 2015 11:09:40 -0500 Subject: selinux: Revalidate invalid inode security labels When fetching an inode's security label, check if it is still valid, and try reloading it if it is not. Reloading will fail when we are in RCU context which doesn't allow sleeping, or when we can't find a dentry for the inode. (Reloading happens via iop->getxattr which takes a dentry parameter.) When reloading fails, continue using the old, invalid label. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 76 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index ef3bd9d61c38..34e3351239d8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -242,11 +242,63 @@ static int inode_alloc_security(struct inode *inode) return 0; } +static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); + +/* + * Try reloading inode security labels that have been marked as invalid. The + * @may_sleep parameter indicates when sleeping and thus reloading labels is + * allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is + * invalid. The @opt_dentry parameter should be set to a dentry of the inode; + * when no dentry is available, set it to NULL instead. + */ +static int __inode_security_revalidate(struct inode *inode, + struct dentry *opt_dentry, + bool may_sleep) +{ + struct inode_security_struct *isec = inode->i_security; + + might_sleep_if(may_sleep); + + if (isec->initialized == LABEL_INVALID) { + if (!may_sleep) + return -ECHILD; + + /* + * Try reloading the inode security label. This will fail if + * @opt_dentry is NULL and no dentry for this inode can be + * found; in that case, continue using the old label. + */ + inode_doinit_with_dentry(inode, opt_dentry); + } + return 0; +} + +static void inode_security_revalidate(struct inode *inode) +{ + __inode_security_revalidate(inode, NULL, true); +} + +static struct inode_security_struct *inode_security_novalidate(struct inode *inode) +{ + return inode->i_security; +} + +static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu) +{ + int error; + + error = __inode_security_revalidate(inode, NULL, !rcu); + if (error) + return ERR_PTR(error); + return inode->i_security; +} + /* * Get the security label of an inode. */ static struct inode_security_struct *inode_security(struct inode *inode) { + __inode_security_revalidate(inode, NULL, true); return inode->i_security; } @@ -257,6 +309,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr { struct inode *inode = d_backing_inode(dentry); + __inode_security_revalidate(inode, dentry, true); return inode->i_security; } @@ -363,8 +416,6 @@ static const char *labeling_behaviors[7] = { "uses native labeling", }; -static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); - static inline int inode_doinit(struct inode *inode) { return inode_doinit_with_dentry(inode, NULL); @@ -1655,6 +1706,7 @@ static inline int dentry_has_perm(const struct cred *cred, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; + __inode_security_revalidate(inode, dentry, true); return inode_has_perm(cred, inode, av, &ad); } @@ -1670,6 +1722,7 @@ static inline int path_has_perm(const struct cred *cred, ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = *path; + __inode_security_revalidate(inode, path->dentry, true); return inode_has_perm(cred, inode, av, &ad); } @@ -2871,7 +2924,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; sid = cred_sid(cred); - isec = inode_security(inode); + isec = inode_security_rcu(inode, rcu); + if (IS_ERR(isec)) + return PTR_ERR(isec); return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad, rcu ? MAY_NOT_BLOCK : 0); @@ -2923,7 +2978,9 @@ static int selinux_inode_permission(struct inode *inode, int mask) perms = file_mask_to_av(inode->i_mode, mask); sid = cred_sid(cred); - isec = inode_security(inode); + isec = inode_security_rcu(inode, flags & MAY_NOT_BLOCK); + if (IS_ERR(isec)) + return PTR_ERR(isec); rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); audited = avc_audit_required(perms, &avd, rc, @@ -3232,6 +3289,7 @@ static int selinux_file_permission(struct file *file, int mask) /* No change since file_open check. */ return 0; + inode_security_revalidate(inode); return selinux_revalidate_file_permission(file, mask); } @@ -3537,6 +3595,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) * new inode label or new policy. * This check is not redundant - do not remove. */ + inode_security_revalidate(file_inode(file)); return file_path_has_perm(cred, file, open_file_to_av(file)); } @@ -4078,7 +4137,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern) { const struct task_security_struct *tsec = current_security(); - struct inode_security_struct *isec = inode_security(SOCK_INODE(sock)); + struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); struct sk_security_struct *sksec; int err = 0; @@ -4278,9 +4337,9 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) if (err) return err; - newisec = inode_security(SOCK_INODE(newsock)); + newisec = inode_security_novalidate(SOCK_INODE(newsock)); - isec = inode_security(SOCK_INODE(sock)); + isec = inode_security_novalidate(SOCK_INODE(sock)); newisec->sclass = isec->sclass; newisec->sid = isec->sid; newisec->initialized = LABEL_INITIALIZED; @@ -4618,7 +4677,8 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid) static void selinux_sock_graft(struct sock *sk, struct socket *parent) { - struct inode_security_struct *isec = inode_security(SOCK_INODE(parent)); + struct inode_security_struct *isec = + inode_security_novalidate(SOCK_INODE(parent)); struct sk_security_struct *sksec = sk->sk_security; if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || -- cgit v1.2.3-55-g7522 From f9df6458218f4fe8a1c3bf0af89c1fa9eaf0db39 Mon Sep 17 00:00:00 2001 From: Andrew Perepechko Date: Thu, 24 Dec 2015 11:09:41 -0500 Subject: selinux: export validatetrans decisions Make validatetrans decisions available through selinuxfs. "/validatetrans" is added to selinuxfs for this purpose. This functionality is needed by file system servers implemented in userspace or kernelspace without the VFS layer. Writing "$oldcontext $newcontext $tclass $taskcontext" to /validatetrans is expected to return 0 if the transition is allowed and -EPERM otherwise. Signed-off-by: Andrew Perepechko CC: andrew.perepechko@seagate.com Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/include/classmap.h | 2 +- security/selinux/include/security.h | 3 ++ security/selinux/selinuxfs.c | 80 +++++++++++++++++++++++++++++++++++++ security/selinux/ss/services.c | 34 ++++++++++++---- 4 files changed, 111 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 5a4eef59aeff..ef83c4b85a33 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -21,7 +21,7 @@ struct security_class_mapping secclass_map[] = { { "compute_av", "compute_create", "compute_member", "check_context", "load_policy", "compute_relabel", "compute_user", "setenforce", "setbool", "setsecparam", - "setcheckreqprot", "read_policy", NULL } }, + "setcheckreqprot", "read_policy", "validate_trans", NULL } }, { "process", { "fork", "transition", "sigchld", "sigkill", "sigstop", "signull", "signal", "ptrace", "getsched", "setsched", diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 223e9fd15d66..38feb55d531a 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -187,6 +187,9 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); +int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid, + u16 tclass); + int security_bounded_transition(u32 oldsid, u32 newsid); int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index c02da25d7b63..0dc407dac3b9 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -116,6 +116,7 @@ enum sel_inos { SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ SEL_STATUS, /* export current status using mmap() */ SEL_POLICY, /* allow userspace to read the in kernel policy */ + SEL_VALIDATE_TRANS, /* compute validatetrans decision */ SEL_INO_NEXT, /* The next inode number to use */ }; @@ -653,6 +654,83 @@ static const struct file_operations sel_checkreqprot_ops = { .llseek = generic_file_llseek, }; +static ssize_t sel_write_validatetrans(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + char *oldcon = NULL, *newcon = NULL, *taskcon = NULL; + char *req = NULL; + u32 osid, nsid, tsid; + u16 tclass; + int rc; + + rc = task_has_security(current, SECURITY__VALIDATE_TRANS); + if (rc) + goto out; + + rc = -ENOMEM; + if (count >= PAGE_SIZE) + goto out; + + /* No partial writes. */ + rc = -EINVAL; + if (*ppos != 0) + goto out; + + rc = -ENOMEM; + req = kzalloc(count + 1, GFP_KERNEL); + if (!req) + goto out; + + rc = -EFAULT; + if (copy_from_user(req, buf, count)) + goto out; + + rc = -ENOMEM; + oldcon = kzalloc(count + 1, GFP_KERNEL); + if (!oldcon) + goto out; + + newcon = kzalloc(count + 1, GFP_KERNEL); + if (!newcon) + goto out; + + taskcon = kzalloc(count + 1, GFP_KERNEL); + if (!taskcon) + goto out; + + rc = -EINVAL; + if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4) + goto out; + + rc = security_context_str_to_sid(oldcon, &osid, GFP_KERNEL); + if (rc) + goto out; + + rc = security_context_str_to_sid(newcon, &nsid, GFP_KERNEL); + if (rc) + goto out; + + rc = security_context_str_to_sid(taskcon, &tsid, GFP_KERNEL); + if (rc) + goto out; + + rc = security_validate_transition_user(osid, nsid, tsid, tclass); + if (!rc) + rc = count; +out: + kfree(req); + kfree(oldcon); + kfree(newcon); + kfree(taskcon); + return rc; +} + +static const struct file_operations sel_transition_ops = { + .write = sel_write_validatetrans, + .llseek = generic_file_llseek, +}; + /* * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c */ @@ -1759,6 +1837,8 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO}, [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, + [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops, + S_IWUGO}, /* last one */ {""} }; ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index ebb5eb3c318c..ebda97333f1b 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -778,8 +778,8 @@ out: return -EPERM; } -int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, - u16 orig_tclass) +static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, + u16 orig_tclass, bool user) { struct context *ocontext; struct context *ncontext; @@ -794,11 +794,12 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, read_lock(&policy_rwlock); - tclass = unmap_class(orig_tclass); + if (!user) + tclass = unmap_class(orig_tclass); + else + tclass = orig_tclass; if (!tclass || tclass > policydb.p_classes.nprim) { - printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", - __func__, tclass); rc = -EINVAL; goto out; } @@ -832,8 +833,13 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, while (constraint) { if (!constraint_expr_eval(ocontext, ncontext, tcontext, constraint->expr)) { - rc = security_validtrans_handle_fail(ocontext, ncontext, - tcontext, tclass); + if (user) + rc = -EPERM; + else + rc = security_validtrans_handle_fail(ocontext, + ncontext, + tcontext, + tclass); goto out; } constraint = constraint->next; @@ -844,6 +850,20 @@ out: return rc; } +int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid, + u16 tclass) +{ + return security_compute_validatetrans(oldsid, newsid, tasksid, + tclass, true); +} + +int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, + u16 orig_tclass) +{ + return security_compute_validatetrans(oldsid, newsid, tasksid, + orig_tclass, false); +} + /* * security_bounded_transition - check whether the given * transition is directed to bounded, or not. -- cgit v1.2.3-55-g7522 From 76319946f321e30872dd72af7de867cb26e7a373 Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Thu, 24 Dec 2015 11:09:41 -0500 Subject: selinux: rate-limit netlink message warnings in selinux_nlmsg_perm() Any process is able to send netlink messages with invalid types. Make the warning rate-limited to prevent too much log spam. The warning is supposed to help to find misbehaving programs, so print the triggering command name and pid. Reported-by: Florian Weimer Signed-off-by: Vladis Dronov [PM: subject line tweak to make checkpatch.pl happy] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 34e3351239d8..40e071af7783 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4858,11 +4858,12 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { - printk(KERN_WARNING - "SELinux: unrecognized netlink message:" - " protocol=%hu nlmsg_type=%hu sclass=%s\n", + pr_warn_ratelimited("SELinux: unrecognized netlink" + " message: protocol=%hu nlmsg_type=%hu sclass=%s" + " pig=%d comm=%s\n", sk->sk_protocol, nlh->nlmsg_type, - secclass_map[sksec->sclass - 1].name); + secclass_map[sksec->sclass - 1].name, + task_pid_nr(current), current->comm); if (!selinux_enforcing || security_get_allow_unknown()) err = 0; } -- cgit v1.2.3-55-g7522 From 0112721df4edbdd07b800813300d76811572f080 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Tue, 22 Dec 2015 08:51:23 -0500 Subject: IMA: policy can be updated zero times Commit "IMA: policy can now be updated multiple times" assumed that the policy would be updated at least once. If there are zero updates, the temporary list head object will get added to the policy list, and later dereferenced as an IMA policy object, which means that invalid memory will be accessed. Changelog: - Move list_empty() test to ima_release_policy(), before audit msg - Mimi Signed-off-by: Sasha Levin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_fs.c | 5 +++++ security/integrity/ima/ima_policy.c | 8 ++++++++ 3 files changed, 14 insertions(+) (limited to 'security') diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 917407fb7e94..585af61ed399 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -166,6 +166,7 @@ void ima_update_policy(void); void ima_update_policy_flag(void); ssize_t ima_parse_add_rule(char *); void ima_delete_rules(void); +int ima_check_policy(void); void *ima_policy_start(struct seq_file *m, loff_t *pos); void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos); void ima_policy_stop(struct seq_file *m, void *v); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index eebb985fd083..3caed6de610c 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -355,6 +355,11 @@ static int ima_release_policy(struct inode *inode, struct file *file) if ((file->f_flags & O_ACCMODE) == O_RDONLY) return 0; + if (valid_policy && ima_check_policy() < 0) { + cause = "failed"; + valid_policy = 0; + } + pr_info("IMA: policy update %s\n", cause); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, "policy_update", cause, !valid_policy, 0); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index ba5d2fc61394..0a3b781f18e5 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -416,6 +416,14 @@ void __init ima_init_policy(void) ima_rules = &ima_default_rules; } +/* Make sure we have a valid policy, at least containing some rules. */ +int ima_check_policy() +{ + if (list_empty(&ima_temp_rules)) + return -EINVAL; + return 0; +} + /** * ima_update_policy - update default_rules with new measure rules * -- cgit v1.2.3-55-g7522 From 6427e6c71c8b374761b661c4f355762794c171a1 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Sun, 3 Jan 2016 17:36:38 +0200 Subject: ima: ima_write_policy() limit locking There is no need to hold the ima_write_mutex for so long. We only need it around ima_parse_add_rule(). Changelog: - The return path now takes into account failed kmalloc() call. Reported-by: Al Viro Signed-off-by: Petko Manolov Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_fs.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 3caed6de610c..f355231997b4 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -261,13 +261,8 @@ static const struct file_operations ima_ascii_measurements_ops = { static ssize_t ima_write_policy(struct file *file, const char __user *buf, size_t datalen, loff_t *ppos) { - char *data = NULL; + char *data; ssize_t result; - int res; - - res = mutex_lock_interruptible(&ima_write_mutex); - if (res) - return res; if (datalen >= PAGE_SIZE) datalen = PAGE_SIZE - 1; @@ -286,14 +281,19 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, result = -EFAULT; if (copy_from_user(data, buf, datalen)) - goto out; + goto out_free; + result = mutex_lock_interruptible(&ima_write_mutex); + if (result < 0) + goto out_free; result = ima_parse_add_rule(data); + mutex_unlock(&ima_write_mutex); + +out_free: + kfree(data); out: if (result < 0) valid_policy = 0; - kfree(data); - mutex_unlock(&ima_write_mutex); return result; } -- cgit v1.2.3-55-g7522 From 1d6d167c2efcfe9539d9cffb1a1be9c92e39c2c0 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Thu, 7 Jan 2016 07:46:36 -0500 Subject: KEYS: refcount bug fix This patch fixes the key_ref leak, removes the unnecessary KEY_FLAG_KEEP test before setting the flag, and cleans up the if/then brackets style introduced in commit: d3600bc KEYS: prevent keys from being removed from specified keyrings Reported-by: David Howells Signed-off-by: Mimi Zohar Acked-by: David Howells --- security/keys/key.c | 3 +-- security/keys/keyctl.c | 17 +++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) (limited to 'security') diff --git a/security/keys/key.c b/security/keys/key.c index 09ef276c4bdc..07a87311055c 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -430,8 +430,7 @@ static int __key_instantiate_and_link(struct key *key, /* and link it into the destination keyring */ if (keyring) { - if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) - set_bit(KEY_FLAG_KEEP, &key->flags); + set_bit(KEY_FLAG_KEEP, &key->flags); __key_link(key, _edit); } diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index e83ec6b9eb9d..8f9f323f372b 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -381,12 +381,11 @@ long keyctl_revoke_key(key_serial_t id) } key = key_ref_to_ptr(key_ref); + ret = 0; if (test_bit(KEY_FLAG_KEEP, &key->flags)) - return -EPERM; - else { + ret = -EPERM; + else key_revoke(key); - ret = 0; - } key_ref_put(key_ref); error: @@ -432,12 +431,11 @@ long keyctl_invalidate_key(key_serial_t id) invalidate: key = key_ref_to_ptr(key_ref); + ret = 0; if (test_bit(KEY_FLAG_KEEP, &key->flags)) ret = -EPERM; - else { + else key_invalidate(key); - ret = 0; - } error_put: key_ref_put(key_ref); error: @@ -1352,12 +1350,11 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) okay: key = key_ref_to_ptr(key_ref); + ret = 0; if (test_bit(KEY_FLAG_KEEP, &key->flags)) ret = -EPERM; - else { + else key_set_timeout(key, timeout); - ret = 0; - } key_put(key); error: -- cgit v1.2.3-55-g7522 From b197367ed1ba81b0d26f7e7f76f61731ac6e5842 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 5 Jan 2016 23:12:33 +0100 Subject: selinux: Inode label revalidation performance fix Commit 5d226df4 has introduced a performance regression of about 10% in the UnixBench pipe benchmark. It turns out that the call to inode_security in selinux_file_permission can be moved below the zero-mask test and that inode_security_revalidate can be removed entirely, which brings us back to roughly the original performance. Signed-off-by: Andreas Gruenbacher Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 40e071af7783..f8110cfd80ff 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -273,11 +273,6 @@ static int __inode_security_revalidate(struct inode *inode, return 0; } -static void inode_security_revalidate(struct inode *inode) -{ - __inode_security_revalidate(inode, NULL, true); -} - static struct inode_security_struct *inode_security_novalidate(struct inode *inode) { return inode->i_security; @@ -3277,19 +3272,19 @@ static int selinux_file_permission(struct file *file, int mask) { struct inode *inode = file_inode(file); struct file_security_struct *fsec = file->f_security; - struct inode_security_struct *isec = inode_security(inode); + struct inode_security_struct *isec; u32 sid = current_sid(); if (!mask) /* No permission to check. Existence test. */ return 0; + isec = inode_security(inode); if (sid == fsec->sid && fsec->isid == isec->sid && fsec->pseqno == avc_policy_seqno()) /* No change since file_open check. */ return 0; - inode_security_revalidate(inode); return selinux_revalidate_file_permission(file, mask); } @@ -3595,7 +3590,6 @@ static int selinux_file_open(struct file *file, const struct cred *cred) * new inode label or new policy. * This check is not redundant - do not remove. */ - inode_security_revalidate(file_inode(file)); return file_path_has_perm(cred, file, open_file_to_av(file)); } -- cgit v1.2.3-55-g7522