diff options
Diffstat (limited to 'security')
50 files changed, 1581 insertions, 598 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index 1832cf701c3d..48071ed7c445 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -137,12 +137,17 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode) { int ret = 0; const struct cred *cred, *child_cred; + const kernel_cap_t *caller_caps; rcu_read_lock(); cred = current_cred(); child_cred = __task_cred(child); + if (mode & PTRACE_MODE_FSCREDS) + caller_caps = &cred->cap_effective; + else + caller_caps = &cred->cap_permitted; if (cred->user_ns == child_cred->user_ns && - cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) + cap_issubset(child_cred->cap_permitted, *caller_caps)) goto out; if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE)) goto out; diff --git a/security/inode.c b/security/inode.c index 16622aef9bde..28414b0207ce 100644 --- a/security/inode.c +++ b/security/inode.c @@ -99,7 +99,7 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode, dir = d_inode(parent); - mutex_lock(&dir->i_mutex); + inode_lock(dir); dentry = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(dentry)) goto out; @@ -129,14 +129,14 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode, } d_instantiate(dentry, inode); dget(dentry); - mutex_unlock(&dir->i_mutex); + inode_unlock(dir); return dentry; out1: dput(dentry); dentry = ERR_PTR(error); out: - mutex_unlock(&dir->i_mutex); + inode_unlock(dir); simple_release_fs(&mount, &mount_count); return dentry; } @@ -195,7 +195,7 @@ void securityfs_remove(struct dentry *dentry) if (!parent || d_really_is_negative(parent)) return; - mutex_lock(&d_inode(parent)->i_mutex); + inode_lock(d_inode(parent)); if (simple_positive(dentry)) { if (d_is_dir(dentry)) simple_rmdir(d_inode(parent), dentry); @@ -203,7 +203,7 @@ void securityfs_remove(struct dentry *dentry) simple_unlink(d_inode(parent), dentry); dput(dentry); } - mutex_unlock(&d_inode(parent)->i_mutex); + inode_unlock(d_inode(parent)); simple_release_fs(&mount, &mount_count); } EXPORT_SYMBOL_GPL(securityfs_remove); diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 73c457bf5a4a..979be65d22c4 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -36,11 +36,22 @@ config INTEGRITY_ASYMMETRIC_KEYS select ASYMMETRIC_KEY_TYPE select ASYMMETRIC_PUBLIC_KEY_SUBTYPE select PUBLIC_KEY_ALGO_RSA + select CRYPTO_RSA select X509_CERTIFICATE_PARSER help 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 + 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/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 4fec1816a2b3..80052ed8d467 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -16,7 +16,9 @@ #include <linux/ratelimit.h> #include <linux/key-type.h> #include <crypto/public_key.h> +#include <crypto/hash_info.h> #include <keys/asymmetric-type.h> +#include <keys/system_keyring.h> #include "integrity.h" @@ -32,9 +34,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)) @@ -80,7 +95,7 @@ int asymmetric_verify(struct key *keyring, const char *sig, if (siglen != __be16_to_cpu(hdr->sig_size)) return -EBADMSG; - if (hdr->hash_algo >= PKEY_HASH__LAST) + if (hdr->hash_algo >= HASH_ALGO__LAST) return -ENOPKG; key = request_asymmetric_key(keyring, __be32_to_cpu(hdr->keyid)); @@ -89,16 +104,13 @@ int asymmetric_verify(struct key *keyring, const char *sig, memset(&pks, 0, sizeof(pks)); - pks.pkey_hash_algo = hdr->hash_algo; + pks.pkey_algo = "rsa"; + pks.hash_algo = hash_algo_name[hdr->hash_algo]; pks.digest = (u8 *)data; pks.digest_size = datalen; - pks.nr_mpi = 1; - pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen); - - if (pks.rsa.s) - ret = verify_signature(key, &pks); - - mpi_free(pks.rsa.s); + pks.s = hdr->sig; + pks.s_size = siglen; + ret = verify_signature(key, &pks); key_put(key); pr_debug("%s() = %d\n", __func__, ret); return ret; diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index bf19723cf117..e825e0ae78e7 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 EVM && 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.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..30b6b7d0429f 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/crypto.h> #include <linux/xattr.h> +#include <linux/evm.h> #include <keys/encrypted-type.h> #include <crypto/hash.h> #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; @@ -40,6 +79,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 { @@ -240,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)) @@ -248,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_main.c b/security/integrity/evm/evm_main.c index 1334e02ae8f4..e6ea9d4b1de9 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -23,6 +23,7 @@ #include <linux/integrity.h> #include <linux/evm.h> #include <crypto/hash.h> +#include <crypto/algapi.h> #include "evm.h" int evm_initialized; @@ -148,7 +149,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, xattr_value_len, calc.digest); if (rc) break; - rc = memcmp(xattr_data->digest, calc.digest, + rc = crypto_memneq(xattr_data->digest, calc.digest, sizeof(calc.digest)); if (rc) rc = -EINVAL; @@ -358,6 +359,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 +388,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 +408,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); } @@ -472,21 +486,34 @@ out: } EXPORT_SYMBOL_GPL(evm_inode_init_security); +#ifdef CONFIG_EVM_LOAD_X509 +void __init evm_load_x509(void) +{ + int rc; + + rc = integrity_load_x509(INTEGRITY_KEYRING_EVM, CONFIG_EVM_X509_PATH); + if (!rc) + evm_initialized |= EVM_INIT_X509; +} +#endif + static int __init init_evm(void) { int error; 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/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index cf12a04717d3..c8dccd54d501 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -62,9 +62,9 @@ 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) + if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_INIT_HMAC)) return -EPERM; if (count >= sizeof(temp) || count == 0) @@ -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 = 1; - pr_info("initialized\n"); - } else - pr_err("initialization failed\n"); + evm_init_key(); + return count; } diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 3d2f5b45c8cb..345b75997e4c 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -77,7 +77,7 @@ static void iint_free(struct integrity_iint_cache *iint) iint->ima_file_status = INTEGRITY_UNKNOWN; iint->ima_mmap_status = INTEGRITY_UNKNOWN; iint->ima_bprm_status = INTEGRITY_UNKNOWN; - iint->ima_module_status = INTEGRITY_UNKNOWN; + iint->ima_read_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; kmem_cache_free(iint_cache, iint); } @@ -157,7 +157,7 @@ static void init_once(void *foo) iint->ima_file_status = INTEGRITY_UNKNOWN; iint->ima_mmap_status = INTEGRITY_UNKNOWN; iint->ima_bprm_status = INTEGRITY_UNKNOWN; - iint->ima_module_status = INTEGRITY_UNKNOWN; + iint->ima_read_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; } @@ -234,12 +234,13 @@ int __init integrity_read_file(const char *path, char **data) } rc = integrity_kernel_read(file, 0, buf, size); - if (rc < 0) - kfree(buf); - else if (rc != size) - rc = -EIO; - else + if (rc == size) { *data = buf; + } else { + kfree(buf); + if (rc >= 0) + rc = -EIO; + } out: fput(file); return rc; @@ -254,4 +255,5 @@ out: void __init integrity_load_keys(void) { ima_load_x509(); + evm_load_x509(); } diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index df303346029b..e54a8a8dae94 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -107,6 +107,27 @@ 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_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 @@ -123,14 +144,35 @@ 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_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.h b/security/integrity/ima/ima.h index e2a60c30df44..5d0f61163d98 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -19,10 +19,12 @@ #include <linux/types.h> #include <linux/crypto.h> +#include <linux/fs.h> #include <linux/security.h> #include <linux/hash.h> #include <linux/tpm.h> #include <linux/audit.h> +#include <crypto/hash_info.h> #include "../integrity.h" @@ -106,6 +108,8 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, const unsigned char *filename); int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); +int ima_calc_buffer_hash(const void *buf, loff_t len, + struct ima_digest_data *hash); int ima_calc_field_array_hash(struct ima_field_data *field_data, struct ima_template_desc *desc, int num_fields, struct ima_digest_data *hash); @@ -136,13 +140,25 @@ static inline unsigned long ima_hash_key(u8 *digest) return hash_long(*digest, IMA_HASH_BITS); } +enum ima_hooks { + FILE_CHECK = 1, + MMAP_CHECK, + BPRM_CHECK, + POST_SETATTR, + MODULE_CHECK, + FIRMWARE_CHECK, + KEXEC_KERNEL_CHECK, + KEXEC_INITRAMFS_CHECK, + POLICY_CHECK, + MAX_CHECK +}; + /* LIM API function definitions */ -int ima_get_action(struct inode *inode, int mask, int function); -int ima_must_measure(struct inode *inode, int mask, int function); +int ima_get_action(struct inode *inode, int mask, enum ima_hooks func); +int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func); int ima_collect_measurement(struct integrity_iint_cache *iint, - struct file *file, - struct evm_ima_xattr_data **xattr_value, - int *xattr_len); + struct file *file, void *buf, loff_t size, + enum hash_algo algo); void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, @@ -157,8 +173,6 @@ void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(struct path *path, char **pathbuf); /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR }; - int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); void ima_init_policy(void); @@ -166,6 +180,11 @@ 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); +int ima_policy_show(struct seq_file *m, void *v); /* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 @@ -173,23 +192,25 @@ void ima_delete_rules(void); #define IMA_APPRAISE_LOG 0x04 #define IMA_APPRAISE_MODULES 0x08 #define IMA_APPRAISE_FIRMWARE 0x10 +#define IMA_APPRAISE_POLICY 0x20 #ifdef CONFIG_IMA_APPRAISE -int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, +int ima_appraise_measurement(enum ima_hooks func, + struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, int opened); int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, - int func); -void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, - struct ima_digest_data *hash); + enum ima_hooks func); +enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, + int xattr_len); int ima_read_xattr(struct dentry *dentry, struct evm_ima_xattr_data **xattr_value); #else -static inline int ima_appraise_measurement(int func, +static inline int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, @@ -211,15 +232,16 @@ static inline void ima_update_xattr(struct integrity_iint_cache *iint, } static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache - *iint, int func) + *iint, + enum ima_hooks func) { return INTEGRITY_UNKNOWN; } -static inline void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, - int xattr_len, - struct ima_digest_data *hash) +static inline enum hash_algo +ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len) { + return ima_hash_algo; } static inline int ima_read_xattr(struct dentry *dentry, @@ -250,17 +272,12 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, { return -EINVAL; } -#endif /* CONFIG_IMA_LSM_RULES */ +#endif /* CONFIG_IMA_TRUSTED_KEYRING */ -#ifdef CONFIG_IMA_TRUSTED_KEYRING -static inline int ima_init_keyring(const unsigned int id) -{ - return integrity_init_keyring(id); -} +#ifdef CONFIG_IMA_READ_POLICY +#define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR) #else -static inline int ima_init_keyring(const unsigned int id) -{ - return 0; -} -#endif /* CONFIG_IMA_TRUSTED_KEYRING */ -#endif +#define POLICY_FILE_FLAGS S_IWUSR +#endif /* CONFIG_IMA_WRITE_POLICY */ + +#endif /* __LINUX_IMA_H */ diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 1d950fbb2aec..370e42dfc5c5 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -18,7 +18,7 @@ #include <linux/fs.h> #include <linux/xattr.h> #include <linux/evm.h> -#include <crypto/hash_info.h> + #include "ima.h" /* @@ -156,7 +156,7 @@ err_out: * ima_get_action - appraise & measure decision based on policy. * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) - * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK) + * @func: caller identifier * * The policy is defined in terms of keypairs: * subj=, obj=, type=, func=, mask=, fsmagic= @@ -168,13 +168,13 @@ err_out: * Returns IMA_MEASURE, IMA_APPRAISE mask. * */ -int ima_get_action(struct inode *inode, int mask, int function) +int ima_get_action(struct inode *inode, int mask, enum ima_hooks func) { int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; flags &= ima_policy_flag; - return ima_match_policy(inode, function, mask, flags); + return ima_match_policy(inode, func, mask, flags); } /* @@ -188,9 +188,8 @@ int ima_get_action(struct inode *inode, int mask, int function) * Return 0 on success, error code otherwise */ int ima_collect_measurement(struct integrity_iint_cache *iint, - struct file *file, - struct evm_ima_xattr_data **xattr_value, - int *xattr_len) + struct file *file, void *buf, loff_t size, + enum hash_algo algo) { const char *audit_cause = "failed"; struct inode *inode = file_inode(file); @@ -201,9 +200,6 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, char digest[IMA_MAX_DIGEST_SIZE]; } hash; - if (xattr_value) - *xattr_len = ima_read_xattr(file->f_path.dentry, xattr_value); - if (!(iint->flags & IMA_COLLECTED)) { u64 i_version = file_inode(file)->i_version; @@ -213,13 +209,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, goto out; } - /* use default hash algorithm */ - hash.hdr.algo = ima_hash_algo; - - if (xattr_value) - ima_get_hash_algo(*xattr_value, *xattr_len, &hash.hdr); + hash.hdr.algo = algo; - result = ima_calc_file_hash(file, &hash.hdr); + result = (!buf) ? ima_calc_file_hash(file, &hash.hdr) : + ima_calc_buffer_hash(buf, size, &hash.hdr); if (!result) { int length = sizeof(hash.hdr) + hash.hdr.length; void *tmpbuf = krealloc(iint->ima_hash, length, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 1873b5536f80..6b4694aedae8 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -15,7 +15,6 @@ #include <linux/magic.h> #include <linux/ima.h> #include <linux/evm.h> -#include <crypto/hash_info.h> #include "ima.h" @@ -68,25 +67,25 @@ static int ima_fix_xattr(struct dentry *dentry, /* Return specific func appraised cached result */ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, - int func) + enum ima_hooks func) { switch (func) { case MMAP_CHECK: return iint->ima_mmap_status; case BPRM_CHECK: return iint->ima_bprm_status; - case MODULE_CHECK: - return iint->ima_module_status; - case FIRMWARE_CHECK: - return iint->ima_firmware_status; case FILE_CHECK: - default: + case POST_SETATTR: return iint->ima_file_status; + case MODULE_CHECK ... MAX_CHECK - 1: + default: + return iint->ima_read_status; } } static void ima_set_cache_status(struct integrity_iint_cache *iint, - int func, enum integrity_status status) + enum ima_hooks func, + enum integrity_status status) { switch (func) { case MMAP_CHECK: @@ -95,20 +94,19 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint, case BPRM_CHECK: iint->ima_bprm_status = status; break; - case MODULE_CHECK: - iint->ima_module_status = status; - break; - case FIRMWARE_CHECK: - iint->ima_firmware_status = status; - break; case FILE_CHECK: - default: + case POST_SETATTR: iint->ima_file_status = status; break; + case MODULE_CHECK ... MAX_CHECK - 1: + default: + iint->ima_read_status = status; + break; } } -static void ima_cache_flags(struct integrity_iint_cache *iint, int func) +static void ima_cache_flags(struct integrity_iint_cache *iint, + enum ima_hooks func) { switch (func) { case MMAP_CHECK: @@ -117,49 +115,51 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func) case BPRM_CHECK: iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED); break; - case MODULE_CHECK: - iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); - break; - case FIRMWARE_CHECK: - iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED); - break; case FILE_CHECK: - default: + case POST_SETATTR: iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); break; + case MODULE_CHECK ... MAX_CHECK - 1: + default: + iint->flags |= (IMA_READ_APPRAISED | IMA_APPRAISED); + break; } } -void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, - struct ima_digest_data *hash) +enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, + int xattr_len) { struct signature_v2_hdr *sig; if (!xattr_value || xattr_len < 2) - return; + /* return default hash algo */ + return ima_hash_algo; switch (xattr_value->type) { case EVM_IMA_XATTR_DIGSIG: sig = (typeof(sig))xattr_value; if (sig->version != 2 || xattr_len <= sizeof(*sig)) - return; - hash->algo = sig->hash_algo; + return ima_hash_algo; + return sig->hash_algo; break; case IMA_XATTR_DIGEST_NG: - hash->algo = xattr_value->digest[0]; + return xattr_value->digest[0]; break; case IMA_XATTR_DIGEST: /* this is for backward compatibility */ if (xattr_len == 21) { unsigned int zero = 0; if (!memcmp(&xattr_value->digest[16], &zero, 4)) - hash->algo = HASH_ALGO_MD5; + return HASH_ALGO_MD5; else - hash->algo = HASH_ALGO_SHA1; + return HASH_ALGO_SHA1; } else if (xattr_len == 17) - hash->algo = HASH_ALGO_MD5; + return HASH_ALGO_MD5; break; } + + /* return default hash algo */ + return ima_hash_algo; } int ima_read_xattr(struct dentry *dentry, @@ -182,7 +182,8 @@ int ima_read_xattr(struct dentry *dentry, * * Return 0 on success, error code otherwise */ -int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, +int ima_appraise_measurement(enum ima_hooks func, + struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, int opened) @@ -296,7 +297,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) if (iint->flags & IMA_DIGSIG) return; - rc = ima_collect_measurement(iint, file, NULL, NULL); + rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo); if (rc < 0) return; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index e24121afb2f2..38f2ed830dd6 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -24,7 +24,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <crypto/hash.h> -#include <crypto/hash_info.h> + #include "ima.h" struct ahash_completion { @@ -126,7 +126,7 @@ static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size, { void *ptr; int order = ima_maxorder; - gfp_t gfp_mask = __GFP_WAIT | __GFP_NOWARN | __GFP_NORETRY; + gfp_t gfp_mask = __GFP_RECLAIM | __GFP_NOWARN | __GFP_NORETRY; if (order) order = min(get_order(max_size), order); @@ -519,6 +519,124 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data, return rc; } +static int calc_buffer_ahash_atfm(const void *buf, loff_t len, + struct ima_digest_data *hash, + struct crypto_ahash *tfm) +{ + struct ahash_request *req; + struct scatterlist sg; + struct ahash_completion res; + int rc, ahash_rc = 0; + + hash->length = crypto_ahash_digestsize(tfm); + + req = ahash_request_alloc(tfm, GFP_KERNEL); + if (!req) + return -ENOMEM; + + init_completion(&res.completion); + ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + ahash_complete, &res); + + rc = ahash_wait(crypto_ahash_init(req), &res); + if (rc) + goto out; + + sg_init_one(&sg, buf, len); + ahash_request_set_crypt(req, &sg, NULL, len); + + ahash_rc = crypto_ahash_update(req); + + /* wait for the update request to complete */ + rc = ahash_wait(ahash_rc, &res); + if (!rc) { + ahash_request_set_crypt(req, NULL, hash->digest, 0); + rc = ahash_wait(crypto_ahash_final(req), &res); + } +out: + ahash_request_free(req); + return rc; +} + +static int calc_buffer_ahash(const void *buf, loff_t len, + struct ima_digest_data *hash) +{ + struct crypto_ahash *tfm; + int rc; + + tfm = ima_alloc_atfm(hash->algo); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc = calc_buffer_ahash_atfm(buf, len, hash, tfm); + + ima_free_atfm(tfm); + + return rc; +} + +static int calc_buffer_shash_tfm(const void *buf, loff_t size, + struct ima_digest_data *hash, + struct crypto_shash *tfm) +{ + SHASH_DESC_ON_STACK(shash, tfm); + unsigned int len; + int rc; + + shash->tfm = tfm; + shash->flags = 0; + + hash->length = crypto_shash_digestsize(tfm); + + rc = crypto_shash_init(shash); + if (rc != 0) + return rc; + + while (size) { + len = size < PAGE_SIZE ? size : PAGE_SIZE; + rc = crypto_shash_update(shash, buf, len); + if (rc) + break; + buf += len; + size -= len; + } + + if (!rc) + rc = crypto_shash_final(shash, hash->digest); + return rc; +} + +static int calc_buffer_shash(const void *buf, loff_t len, + struct ima_digest_data *hash) +{ + struct crypto_shash *tfm; + int rc; + + tfm = ima_alloc_tfm(hash->algo); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc = calc_buffer_shash_tfm(buf, len, hash, tfm); + + ima_free_tfm(tfm); + return rc; +} + +int ima_calc_buffer_hash(const void *buf, loff_t len, + struct ima_digest_data *hash) +{ + int rc; + + if (ima_ahash_minsize && len >= ima_ahash_minsize) { + rc = calc_buffer_ahash(buf, len, hash); + if (!rc) + return 0; + } + + return calc_buffer_shash(buf, len, hash); +} + static void __init ima_pcrread(int idx, u8 *pcr) { if (!ima_used_chip) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 816d175da79a..60d011aaec38 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -22,9 +22,12 @@ #include <linux/rculist.h> #include <linux/rcupdate.h> #include <linux/parser.h> +#include <linux/vmalloc.h> #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, @@ -256,10 +259,47 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, }; +static ssize_t ima_read_policy(char *path) +{ + void *data; + char *datap; + loff_t size; + int rc, pathlen = strlen(path); + + char *p; + + /* remove \n */ + datap = path; + strsep(&datap, "\n"); + + rc = kernel_read_file_from_path(path, &data, &size, 0, READING_POLICY); + if (rc < 0) { + pr_err("Unable to open file: %s (%d)", path, rc); + return rc; + } + + datap = data; + while (size > 0 && (p = strsep(&datap, "\n"))) { + pr_debug("rule: %s\n", p); + rc = ima_parse_add_rule(p); + if (rc < 0) + break; + size -= rc; + } + + vfree(data); + if (rc < 0) + return rc; + else if (size) + return -EINVAL; + else + return pathlen; +} + 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; if (datalen >= PAGE_SIZE) @@ -279,13 +319,31 @@ 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 = ima_parse_add_rule(data); + result = mutex_lock_interruptible(&ima_write_mutex); + if (result < 0) + goto out_free; + + if (data[0] == '/') { + result = ima_read_policy(data); + } else if (ima_appraise & IMA_APPRAISE_POLICY) { + pr_err("IMA: signed policy file (specified as an absolute pathname) required\n"); + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, + "policy_update", "signed policy required", + 1, 0); + if (ima_appraise & IMA_APPRAISE_ENFORCE) + result = -EACCES; + } else { + result = ima_parse_add_rule(data); + } + mutex_unlock(&ima_write_mutex); +out_free: + kfree(data); out: if (result < 0) valid_policy = 0; - kfree(data); + return result; } @@ -302,14 +360,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; @@ -326,6 +401,14 @@ 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; + + 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); @@ -336,15 +419,21 @@ 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); ima_policy = NULL; +#else + clear_bit(IMA_FS_BUSY, &ima_fs_flags); +#endif return 0; } 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, }; @@ -382,8 +471,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_init.c b/security/integrity/ima/ima_init.c index e600cadd231c..5d679a685616 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -21,7 +21,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/err.h> -#include <crypto/hash_info.h> + #include "ima.h" /* name for boot aggregate entry */ @@ -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/ima/ima_main.c b/security/integrity/ima/ima_main.c index c21f09bf8b99..391f41751021 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -24,7 +24,6 @@ #include <linux/slab.h> #include <linux/xattr.h> #include <linux/ima.h> -#include <crypto/hash_info.h> #include "ima.h" @@ -121,7 +120,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, if (!(mode & FMODE_WRITE)) return; - mutex_lock(&inode->i_mutex); + inode_lock(inode); if (atomic_read(&inode->i_writecount) == 1) { if ((iint->version != inode->i_version) || (iint->flags & IMA_NEW_FILE)) { @@ -130,7 +129,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, ima_update_xattr(iint, file); } } - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); } /** @@ -154,8 +153,8 @@ void ima_file_free(struct file *file) ima_check_last_writer(iint, inode, file); } -static int process_measurement(struct file *file, int mask, int function, - int opened) +static int process_measurement(struct file *file, char *buf, loff_t size, + int mask, enum ima_hooks func, int opened) { struct inode *inode = file_inode(file); struct integrity_iint_cache *iint = NULL; @@ -163,9 +162,10 @@ static int process_measurement(struct file *file, int mask, int function, char *pathbuf = NULL; const char *pathname = NULL; int rc = -ENOMEM, action, must_appraise; - struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; + struct evm_ima_xattr_data *xattr_value = NULL; int xattr_len = 0; bool violation_check; + enum hash_algo hash_algo; if (!ima_policy_flag || !S_ISREG(inode->i_mode)) return 0; @@ -174,8 +174,8 @@ static int process_measurement(struct file *file, int mask, int function, * bitmask based on the appraise/audit/measurement policy. * Included is the appraise submask. */ - action = ima_get_action(inode, mask, function); - violation_check = ((function == FILE_CHECK || function == MMAP_CHECK) && + action = ima_get_action(inode, mask, func); + violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) && (ima_policy_flag & IMA_MEASURE)); if (!action && !violation_check) return 0; @@ -184,9 +184,9 @@ static int process_measurement(struct file *file, int mask, int function, /* Is the appraise rule hook specific? */ if (action & IMA_FILE_APPRAISE) - function = FILE_CHECK; + func = FILE_CHECK; - mutex_lock(&inode->i_mutex); + inode_lock(inode); if (action) { iint = integrity_inode_get(inode); @@ -214,16 +214,19 @@ static int process_measurement(struct file *file, int mask, int function, /* Nothing to do, just return existing appraised status */ if (!action) { if (must_appraise) - rc = ima_get_cache_status(iint, function); + rc = ima_get_cache_status(iint, func); goto out_digsig; } template_desc = ima_template_desc_current(); if ((action & IMA_APPRAISE_SUBMASK) || strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) - xattr_ptr = &xattr_value; + /* read 'security.ima' */ + xattr_len = ima_read_xattr(file->f_path.dentry, &xattr_value); - rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); + hash_algo = ima_get_hash_algo(xattr_value, xattr_len); + + rc = ima_collect_measurement(iint, file, buf, size, hash_algo); if (rc != 0) { if (file->f_flags & O_DIRECT) rc = (iint->flags & IMA_PERMIT_DIRECTIO) ? 0 : -EACCES; @@ -237,7 +240,7 @@ static int process_measurement(struct file *file, int mask, int function, ima_store_measurement(iint, file, pathname, xattr_value, xattr_len); if (action & IMA_APPRAISE_SUBMASK) - rc = ima_appraise_measurement(function, iint, file, pathname, + rc = ima_appraise_measurement(func, iint, file, pathname, xattr_value, xattr_len, opened); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); @@ -250,7 +253,7 @@ out_free: if (pathbuf) __putname(pathbuf); out: - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) return -EACCES; return 0; @@ -270,7 +273,8 @@ out: int ima_file_mmap(struct file *file, unsigned long prot) { if (file && (prot & PROT_EXEC)) - return process_measurement(file, MAY_EXEC, MMAP_CHECK, 0); + return process_measurement(file, NULL, 0, MAY_EXEC, + MMAP_CHECK, 0); return 0; } @@ -289,7 +293,8 @@ int ima_file_mmap(struct file *file, unsigned long prot) */ int ima_bprm_check(struct linux_binprm *bprm) { - return process_measurement(bprm->file, MAY_EXEC, BPRM_CHECK, 0); + return process_measurement(bprm->file, NULL, 0, MAY_EXEC, + BPRM_CHECK, 0); } /** @@ -304,24 +309,26 @@ int ima_bprm_check(struct linux_binprm *bprm) */ int ima_file_check(struct file *file, int mask, int opened) { - return process_measurement(file, + return process_measurement(file, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK, opened); } EXPORT_SYMBOL_GPL(ima_file_check); /** - * ima_module_check - based on policy, collect/store/appraise measurement. - * @file: pointer to the file to be measured/appraised + * ima_read_file - pre-measure/appraise hook decision based on policy + * @file: pointer to the file to be measured/appraised/audit + * @read_id: caller identifier * - * Measure/appraise kernel modules based on policy. + * Permit reading a file based on policy. The policy rules are written + * in terms of the policy identifier. Appraising the integrity of + * a file requires a file descriptor. * - * On success return 0. On integrity appraisal error, assuming the file - * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. + * For permission return 0, otherwise return -EACCES. */ -int ima_module_check(struct file *file) +int ima_read_file(struct file *file, enum kernel_read_file_id read_id) { - if (!file) { + if (!file && read_id == READING_MODULE) { #ifndef CONFIG_MODULE_SIG_FORCE if ((ima_appraise & IMA_APPRAISE_MODULES) && (ima_appraise & IMA_APPRAISE_ENFORCE)) @@ -329,18 +336,53 @@ int ima_module_check(struct file *file) #endif return 0; /* We rely on module signature checking */ } - return process_measurement(file, MAY_EXEC, MODULE_CHECK, 0); + return 0; } -int ima_fw_from_file(struct file *file, char *buf, size_t size) +static int read_idmap[READING_MAX_ID] = { + [READING_FIRMWARE] = FIRMWARE_CHECK, + [READING_MODULE] = MODULE_CHECK, + [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, + [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, + [READING_POLICY] = POLICY_CHECK +}; + +/** + * ima_post_read_file - in memory collect/appraise/audit measurement + * @file: pointer to the file to be measured/appraised/audit + * @buf: pointer to in memory file contents + * @size: size of in memory file contents + * @read_id: caller identifier + * + * Measure/appraise/audit in memory file based on policy. Policy rules + * are written in terms of a policy identifier. + * + * On success return 0. On integrity appraisal error, assuming the file + * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. + */ +int ima_post_read_file(struct file *file, void *buf, loff_t size, + enum kernel_read_file_id read_id) { - if (!file) { + enum ima_hooks func; + + if (!file && read_id == READING_FIRMWARE) { if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && (ima_appraise & IMA_APPRAISE_ENFORCE)) return -EACCES; /* INTEGRITY_UNKNOWN */ return 0; } - return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, 0); + + if (!file && read_id == READING_MODULE) /* MODULE_SIG_FORCE enabled */ + return 0; + + if (!file || !buf || size == 0) { /* should never happen */ + if (ima_appraise & IMA_APPRAISE_ENFORCE) + return -EACCES; + return 0; + } + + func = read_idmap[read_id] ?: FILE_CHECK; + return process_measurement(file, buf, size, MAY_READ, func, 0); } static int __init init_ima(void) diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c new file mode 100644 index 000000000000..676885e4320e --- /dev/null +++ b/security/integrity/ima/ima_mok.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 Juniper Networks, Inc. + * + * Author: + * Petko Manolov <petko.manolov@konsulko.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + */ + +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/cred.h> +#include <linux/err.h> +#include <linux/init.h> +#include <keys/asymmetric-type.h> + + +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); + set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); + return 0; +} +device_initcall(ima_mok_init); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3997e206f82d..be09e2cacf82 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -12,11 +12,14 @@ */ #include <linux/module.h> #include <linux/list.h> +#include <linux/fs.h> #include <linux/security.h> #include <linux/magic.h> #include <linux/parser.h> #include <linux/slab.h> +#include <linux/rculist.h> #include <linux/genhd.h> +#include <linux/seq_file.h> #include "ima.h" @@ -38,6 +41,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, @@ -110,6 +114,7 @@ static struct ima_rule_entry default_measurement_rules[] = { .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_INMASK | IMA_UID}, {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, + {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, }; static struct ima_rule_entry default_appraise_rules[] = { @@ -124,6 +129,10 @@ static struct ima_rule_entry default_appraise_rules[] = { {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +#ifdef CONFIG_IMA_WRITE_POLICY + {.action = APPRAISE, .func = POLICY_CHECK, + .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, +#endif #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER}, #else @@ -135,11 +144,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 +180,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 +202,6 @@ static void ima_lsm_update_rules(void) BUG_ON(!entry->lsm[i].rule); } } - mutex_unlock(&ima_rules_mutex); } /** @@ -208,8 +213,8 @@ static void ima_lsm_update_rules(void) * * Returns true on rule match, false on failure. */ -static bool ima_match_rules(struct ima_rule_entry *rule, - struct inode *inode, enum ima_hooks func, int mask) +static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, + enum ima_hooks func, int mask) { struct task_struct *tsk = current; const struct cred *cred = current_cred(); @@ -290,7 +295,7 @@ retry: * In addition to knowing that we need to appraise the file in general, * we need to differentiate between calling hooks, for hook specific rules. */ -static int get_subaction(struct ima_rule_entry *rule, int func) +static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) { if (!(rule->flags & IMA_FUNC)) return IMA_FILE_APPRAISE; @@ -300,13 +305,12 @@ static int get_subaction(struct ima_rule_entry *rule, int func) return IMA_MMAP_APPRAISE; case BPRM_CHECK: return IMA_BPRM_APPRAISE; - case MODULE_CHECK: - return IMA_MODULE_APPRAISE; - case FIRMWARE_CHECK: - return IMA_FIRMWARE_APPRAISE; case FILE_CHECK: - default: + case POST_SETATTR: return IMA_FILE_APPRAISE; + case MODULE_CHECK ... MAX_CHECK - 1: + default: + return IMA_READ_APPRAISE; } } @@ -319,9 +323,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 +333,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 +356,7 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, if (!actmask) break; } + rcu_read_unlock(); return action; } @@ -365,12 +371,12 @@ 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; } + ima_appraise |= temp_ima_appraise; if (!ima_appraise) ima_policy_flag &= ~IMA_APPRAISE; } @@ -410,21 +416,56 @@ void __init ima_init_policy(void) for (i = 0; i < appraise_entries; i++) { list_add_tail(&default_appraise_rules[i].list, &ima_default_rules); + if (default_appraise_rules[i].func == POLICY_CHECK) + temp_ima_appraise |= IMA_APPRAISE_POLICY; } ima_rules = &ima_default_rules; + ima_update_policy_flag(); +} + +/* Make sure we have a valid policy, at least containing some rules. */ +int ima_check_policy(void) +{ + if (list_empty(&ima_temp_rules)) + return -EINVAL; + return 0; } /** * 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(); } @@ -436,8 +477,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 = { @@ -579,6 +620,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = MMAP_CHECK; else if (strcmp(args[0].from, "BPRM_CHECK") == 0) entry->func = BPRM_CHECK; + else if (strcmp(args[0].from, "KEXEC_KERNEL_CHECK") == + 0) + entry->func = KEXEC_KERNEL_CHECK; + else if (strcmp(args[0].from, "KEXEC_INITRAMFS_CHECK") + == 0) + entry->func = KEXEC_INITRAMFS_CHECK; + else if (strcmp(args[0].from, "POLICY_CHECK") == 0) + entry->func = POLICY_CHECK; else result = -EINVAL; if (!result) @@ -734,9 +783,11 @@ 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; + else if (entry->func == POLICY_CHECK) + temp_ima_appraise |= IMA_APPRAISE_POLICY; audit_log_format(ab, "res=%d", !result); audit_log_end(ab); return result; @@ -746,7 +797,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 +833,253 @@ 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) { + 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); list_del(&entry->list); kfree(entry); } - mutex_unlock(&ima_rules_mutex); } + +#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, + func_kexec_kernel, func_kexec_initramfs, + func_policy +}; + +static char *func_tokens[] = { + "FILE_CHECK", + "MMAP_CHECK", + "BPRM_CHECK", + "MODULE_CHECK", + "FIRMWARE_CHECK", + "KEXEC_KERNEL_CHECK", + "KEXEC_INITRAMFS_CHECK", + "POLICY_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] + +/* + * policy_func_show - display the ima_hooks policy rule + */ +static void policy_func_show(struct seq_file *m, enum ima_hooks func) +{ + char tbuf[64] = {0,}; + + switch (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; + case KEXEC_KERNEL_CHECK: + seq_printf(m, pt(Opt_func), ft(func_kexec_kernel)); + break; + case KEXEC_INITRAMFS_CHECK: + seq_printf(m, pt(Opt_func), ft(func_kexec_initramfs)); + break; + case POLICY_CHECK: + seq_printf(m, pt(Opt_func), ft(func_policy)); + break; + default: + snprintf(tbuf, sizeof(tbuf), "%d", func); + seq_printf(m, pt(Opt_func), tbuf); + break; + } + seq_puts(m, " "); +} + +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) + policy_func_show(m, entry->func); + + 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 */ diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 0b7404ebfa80..febd12ed9b55 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -15,8 +15,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <crypto/hash_info.h> - #include "ima.h" #include "ima_template_lib.h" diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 2934e3d377f1..f9bae04ba176 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -12,7 +12,6 @@ * File: ima_template_lib.c * Library of supported template fields. */ -#include <crypto/hash_info.h> #include "ima_template_lib.h" diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 9c6168709d3b..e08935cf343f 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -45,16 +45,12 @@ #define IMA_MMAP_APPRAISED 0x00000800 #define IMA_BPRM_APPRAISE 0x00001000 #define IMA_BPRM_APPRAISED 0x00002000 -#define IMA_MODULE_APPRAISE 0x00004000 -#define IMA_MODULE_APPRAISED 0x00008000 -#define IMA_FIRMWARE_APPRAISE 0x00010000 -#define IMA_FIRMWARE_APPRAISED 0x00020000 +#define IMA_READ_APPRAISE 0x00004000 +#define IMA_READ_APPRAISED 0x00008000 #define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \ - IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE | \ - IMA_FIRMWARE_APPRAISE) + IMA_BPRM_APPRAISE | IMA_READ_APPRAISE) #define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \ - IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED | \ - IMA_FIRMWARE_APPRAISED) + IMA_BPRM_APPRAISED | IMA_READ_APPRAISED) enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, @@ -94,7 +90,7 @@ struct ima_digest_data { struct signature_v2_hdr { uint8_t type; /* xattr type */ uint8_t version; /* signature format version */ - uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */ + uint8_t hash_algo; /* Digest algorithm [enum hash_algo] */ uint32_t keyid; /* IMA key identifier - not X509/PGP specific */ uint16_t sig_size; /* signature size */ uint8_t sig[0]; /* signature payload */ @@ -109,8 +105,7 @@ struct integrity_iint_cache { enum integrity_status ima_file_status:4; enum integrity_status ima_mmap_status:4; enum integrity_status ima_bprm_status:4; - enum integrity_status ima_module_status:4; - enum integrity_status ima_firmware_status:4; + enum integrity_status ima_read_status:4; enum integrity_status evm_status:4; struct ima_digest_data *ima_hash; }; @@ -125,8 +120,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 +144,6 @@ static inline int integrity_init_keyring(const unsigned int id) { return 0; } - #endif /* CONFIG_INTEGRITY_SIGNATURE */ #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS @@ -171,6 +165,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, 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/big_key.c b/security/keys/big_key.c index 907c1522ee46..c721e398893a 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -9,7 +9,6 @@ * 2 of the Licence, or (at your option) any later version. */ -#include <linux/module.h> #include <linux/init.h> #include <linux/seq_file.h> #include <linux/file.h> @@ -18,8 +17,6 @@ #include <keys/user-type.h> #include <keys/big_key-type.h> -MODULE_LICENSE("GPL"); - /* * Layout of key payload words. */ @@ -212,18 +209,8 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) return ret; } -/* - * Module stuff - */ static int __init big_key_init(void) { return register_key_type(&key_type_big_key); } - -static void __exit big_key_cleanup(void) -{ - unregister_key_type(&key_type_big_key); -} - -module_init(big_key_init); -module_exit(big_key_cleanup); +device_initcall(big_key_init); diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 927db9f35ad6..5adbfc32242f 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -28,11 +28,10 @@ #include <linux/random.h> #include <linux/rcupdate.h> #include <linux/scatterlist.h> -#include <linux/crypto.h> #include <linux/ctype.h> #include <crypto/hash.h> #include <crypto/sha.h> -#include <crypto/aes.h> +#include <crypto/skcipher.h> #include "encrypted.h" #include "ecryptfs_format.h" @@ -85,17 +84,17 @@ static const match_table_t key_tokens = { static int aes_get_sizes(void) { - struct crypto_blkcipher *tfm; + struct crypto_skcipher *tfm; - tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); + tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) { pr_err("encrypted_key: failed to alloc_cipher (%ld)\n", PTR_ERR(tfm)); return PTR_ERR(tfm); } - ivsize = crypto_blkcipher_ivsize(tfm); - blksize = crypto_blkcipher_blocksize(tfm); - crypto_free_blkcipher(tfm); + ivsize = crypto_skcipher_ivsize(tfm); + blksize = crypto_skcipher_blocksize(tfm); + crypto_free_skcipher(tfm); return 0; } @@ -401,28 +400,37 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, return ret; } -static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key, - unsigned int key_len, const u8 *iv, - unsigned int ivsize) +static struct skcipher_request *init_skcipher_req(const u8 *key, + unsigned int key_len) { + struct skcipher_request *req; + struct crypto_skcipher *tfm; int ret; - desc->tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(desc->tfm)) { + tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { pr_err("encrypted_key: failed to load %s transform (%ld)\n", - blkcipher_alg, PTR_ERR(desc->tfm)); - return PTR_ERR(desc->tfm); + blkcipher_alg, PTR_ERR(tfm)); + return ERR_CAST(tfm); } - desc->flags = 0; - ret = crypto_blkcipher_setkey(desc->tfm, key, key_len); + ret = crypto_skcipher_setkey(tfm, key, key_len); if (ret < 0) { pr_err("encrypted_key: failed to setkey (%d)\n", ret); - crypto_free_blkcipher(desc->tfm); - return ret; + crypto_free_skcipher(tfm); + return ERR_PTR(ret); } - crypto_blkcipher_set_iv(desc->tfm, iv, ivsize); - return 0; + + req = skcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + pr_err("encrypted_key: failed to allocate request for %s\n", + blkcipher_alg); + crypto_free_skcipher(tfm); + return ERR_PTR(-ENOMEM); + } + + skcipher_request_set_callback(req, 0, NULL, NULL); + return req; } static struct key *request_master_key(struct encrypted_key_payload *epayload, @@ -467,7 +475,8 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, { struct scatterlist sg_in[2]; struct scatterlist sg_out[1]; - struct blkcipher_desc desc; + struct crypto_skcipher *tfm; + struct skcipher_request *req; unsigned int encrypted_datalen; unsigned int padlen; char pad[16]; @@ -476,9 +485,9 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); padlen = encrypted_datalen - epayload->decrypted_datalen; - ret = init_blkcipher_desc(&desc, derived_key, derived_keylen, - epayload->iv, ivsize); - if (ret < 0) + req = init_skcipher_req(derived_key, derived_keylen); + ret = PTR_ERR(req); + if (IS_ERR(req)) goto out; dump_decrypted_data(epayload); @@ -491,8 +500,12 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, sg_init_table(sg_out, 1); sg_set_buf(sg_out, epayload->encrypted_data, encrypted_datalen); - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datalen); - crypto_free_blkcipher(desc.tfm); + skcipher_request_set_crypt(req, sg_in, sg_out, encrypted_datalen, + epayload->iv); + ret = crypto_skcipher_encrypt(req); + tfm = crypto_skcipher_reqtfm(req); + skcipher_request_free(req); + crypto_free_skcipher(tfm); if (ret < 0) pr_err("encrypted_key: failed to encrypt (%d)\n", ret); else @@ -565,15 +578,16 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload, { struct scatterlist sg_in[1]; struct scatterlist sg_out[2]; - struct blkcipher_desc desc; + struct crypto_skcipher *tfm; + struct skcipher_request *req; unsigned int encrypted_datalen; char pad[16]; int ret; encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); - ret = init_blkcipher_desc(&desc, derived_key, derived_keylen, - epayload->iv, ivsize); - if (ret < 0) + req = init_skcipher_req(derived_key, derived_keylen); + ret = PTR_ERR(req); + if (IS_ERR(req)) goto out; dump_encrypted_data(epayload, encrypted_datalen); @@ -585,8 +599,12 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload, epayload->decrypted_datalen); sg_set_buf(&sg_out[1], pad, sizeof pad); - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datalen); - crypto_free_blkcipher(desc.tfm); + skcipher_request_set_crypt(req, sg_in, sg_out, encrypted_datalen, + epayload->iv); + ret = crypto_skcipher_decrypt(req); + tfm = crypto_skcipher_reqtfm(req); + skcipher_request_free(req); + crypto_free_skcipher(tfm); if (ret < 0) goto out; dump_decrypted_data(epayload); @@ -845,6 +863,8 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) size_t datalen = prep->datalen; int ret = 0; + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + return -ENOKEY; if (datalen <= 0 || datalen > 32767 || !prep->data) return -EINVAL; diff --git a/security/keys/key.c b/security/keys/key.c index ab7997ded725..b28755131687 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -296,6 +296,8 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->flags |= 1 << KEY_FLAG_IN_QUOTA; if (flags & KEY_ALLOC_TRUSTED) key->flags |= 1 << KEY_FLAG_TRUSTED; + if (flags & KEY_ALLOC_BUILT_IN) + key->flags |= 1 << KEY_FLAG_BUILTIN; #ifdef KEY_DEBUGGING key->magic = KEY_DEBUG_MAGIC; @@ -429,8 +431,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..ed73c6c1c326 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,12 @@ long keyctl_revoke_key(key_serial_t id) } } - key_revoke(key_ref_to_ptr(key_ref)); + key = key_ref_to_ptr(key_ref); ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + key_revoke(key); key_ref_put(key_ref); error: @@ -392,11 +399,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 +430,12 @@ long keyctl_invalidate_key(key_serial_t id) } invalidate: - key_invalidate(key_ref_to_ptr(key_ref)); + key = key_ref_to_ptr(key_ref); ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + key_invalidate(key); error_put: key_ref_put(key_ref); error: @@ -433,12 +447,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 +475,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 +530,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 +552,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: @@ -751,16 +779,16 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) /* the key is probably readable - now try to read it */ can_read_key: - ret = key_validate(key); - if (ret == 0) { - ret = -EOPNOTSUPP; - if (key->type->read) { - /* read the data with the semaphore held (since we - * might sleep) */ - down_read(&key->sem); + ret = -EOPNOTSUPP; + if (key->type->read) { + /* Read the data with the semaphore held (since we might sleep) + * to protect against the key being updated or revoked. + */ + down_read(&key->sem); + ret = key_validate(key); + if (ret == 0) ret = key->type->read(key, buffer, buflen); - up_read(&key->sem); - } + up_read(&key->sem); } error2: @@ -1289,6 +1317,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 +1350,13 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) okay: key = key_ref_to_ptr(key_ref); - key_set_timeout(key, timeout); + ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + key_set_timeout(key, timeout); key_put(key); - ret = 0; error: return ret; } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index a3f85d2a00bb..e6d50172872f 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -794,6 +794,7 @@ long join_session_keyring(const char *name) ret = PTR_ERR(keyring); goto error2; } else if (keyring == new->session_keyring) { + key_put(keyring); ret = 0; goto error2; } diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 903dace648a1..90d61751ff12 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -11,6 +11,7 @@ * See Documentation/security/keys-trusted-encrypted.txt */ +#include <crypto/hash_info.h> #include <linux/uaccess.h> #include <linux/module.h> #include <linux/init.h> @@ -710,7 +711,10 @@ 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, + Opt_policydigest, + Opt_policyhandle, }; static const match_table_t key_tokens = { @@ -723,6 +727,9 @@ static const match_table_t key_tokens = { {Opt_pcrinfo, "pcrinfo=%s"}, {Opt_pcrlock, "pcrlock=%s"}, {Opt_migratable, "migratable=%s"}, + {Opt_hash, "hash=%s"}, + {Opt_policydigest, "policydigest=%s"}, + {Opt_policyhandle, "policyhandle=%s"}, {Opt_err, NULL} }; @@ -736,11 +743,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay, int res; unsigned long handle; unsigned long lock; + unsigned long token_mask = 0; + unsigned int digest_len; + 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') continue; token = match_token(p, key_tokens, args); + if (test_and_set_bit(token, &token_mask)) + return -EINVAL; switch (token) { case Opt_pcrinfo: @@ -787,6 +806,40 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; 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; + 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; + case Opt_policydigest: + digest_len = hash_digest_size[opt->hash]; + if (!tpm2 || strlen(args[0].from) != (2 * digest_len)) + return -EINVAL; + res = hex2bin(opt->policydigest, args[0].from, + digest_len); + if (res < 0) + return -EINVAL; + opt->policydigest_len = digest_len; + 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; } @@ -1007,13 +1060,16 @@ static void trusted_rcu_free(struct rcu_head *rcu) */ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) { - struct trusted_key_payload *p = key->payload.data[0]; + struct trusted_key_payload *p; struct trusted_key_payload *new_p; struct trusted_key_options *new_o; size_t datalen = prep->datalen; char *datablob; int ret = 0; + if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + return -ENOKEY; + p = key->payload.data[0]; if (!p->migratable) return -EPERM; if (datalen <= 0 || datalen > 32767 || !prep->data) diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 28cb30f80256..8705d79b2c6f 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -120,7 +120,10 @@ int user_update(struct key *key, struct key_preparsed_payload *prep) if (ret == 0) { /* attach the new data, displacing the old */ - zap = key->payload.data[0]; + if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + zap = key->payload.data[0]; + else + zap = NULL; rcu_assign_keypointer(key, upayload); key->expiry = 0; } diff --git a/security/security.c b/security/security.c index 46f405ce6b0f..3644b0344d29 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; @@ -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); } @@ -884,31 +884,33 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) return call_int_hook(kernel_create_files_as, 0, new, inode); } -int security_kernel_fw_from_file(struct file *file, char *buf, size_t size) +int security_kernel_module_request(char *kmod_name) +{ + return call_int_hook(kernel_module_request, 0, kmod_name); +} + +int security_kernel_read_file(struct file *file, enum kernel_read_file_id id) { int ret; - ret = call_int_hook(kernel_fw_from_file, 0, file, buf, size); + ret = call_int_hook(kernel_read_file, 0, file, id); if (ret) return ret; - return ima_fw_from_file(file, buf, size); + return ima_read_file(file, id); } -EXPORT_SYMBOL_GPL(security_kernel_fw_from_file); +EXPORT_SYMBOL_GPL(security_kernel_read_file); -int security_kernel_module_request(char *kmod_name) -{ - return call_int_hook(kernel_module_request, 0, kmod_name); -} - -int security_kernel_module_from_file(struct file *file) +int security_kernel_post_read_file(struct file *file, char *buf, loff_t size, + enum kernel_read_file_id id) { int ret; - ret = call_int_hook(kernel_module_from_file, 0, file); + ret = call_int_hook(kernel_post_read_file, 0, file, buf, size, id); if (ret) return ret; - return ima_module_check(file); + return ima_post_read_file(file, buf, size, id); } +EXPORT_SYMBOL_GPL(security_kernel_post_read_file); int security_task_fix_setuid(struct cred *new, const struct cred *old, int flags) @@ -1161,6 +1163,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); @@ -1685,12 +1693,12 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.kernel_act_as), .kernel_create_files_as = LIST_HEAD_INIT(security_hook_heads.kernel_create_files_as), - .kernel_fw_from_file = - LIST_HEAD_INIT(security_hook_heads.kernel_fw_from_file), .kernel_module_request = LIST_HEAD_INIT(security_hook_heads.kernel_module_request), - .kernel_module_from_file = - LIST_HEAD_INIT(security_hook_heads.kernel_module_from_file), + .kernel_read_file = + LIST_HEAD_INIT(security_hook_heads.kernel_read_file), + .kernel_post_read_file = + LIST_HEAD_INIT(security_hook_heads.kernel_post_read_file), .task_fix_setuid = LIST_HEAD_INIT(security_hook_heads.task_fix_setuid), .task_setpgid = LIST_HEAD_INIT(security_hook_heads.task_setpgid), @@ -1763,6 +1771,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/Makefile b/security/selinux/Makefile index ad5cd76ec231..3411c33e2a44 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -13,7 +13,7 @@ selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o selinux-$(CONFIG_NETLABEL) += netlabel.o -ccflags-y := -Isecurity/selinux -Isecurity/selinux/include +ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9e591e5989be..912deee3f01e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -242,6 +242,72 @@ 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 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; +} + +/* + * 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); + + __inode_security_revalidate(inode, dentry, true); + return inode->i_security; +} + static void inode_free_rcu(struct rcu_head *head) { struct inode_security_struct *isec; @@ -345,8 +411,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); @@ -565,8 +629,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 +685,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; @@ -802,7 +866,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) { @@ -852,8 +916,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 +967,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; } @@ -1293,11 +1354,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; @@ -1469,7 +1530,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); @@ -1640,6 +1701,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); } @@ -1655,6 +1717,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); } @@ -1712,13 +1775,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 +1810,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 +1863,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 +1907,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 +1938,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 +2074,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 +2091,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 +2280,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; @@ -2352,7 +2415,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, tty = get_current_tty(); if (tty) { - spin_lock(&tty_files_lock); + spin_lock(&tty->files_lock); if (!list_empty(&tty->tty_files)) { struct tty_file_private *file_priv; @@ -2367,7 +2430,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, if (file_path_has_perm(cred, file, FILE__READ | FILE__WRITE)) drop_tty = 1; } - spin_unlock(&tty_files_lock); + spin_unlock(&tty->files_lock); tty_kref_put(tty); } /* Reset controlling tty. */ @@ -2639,7 +2702,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; @@ -2753,13 +2816,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; @@ -2777,7 +2838,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)) @@ -2858,7 +2919,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->i_security; + 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); @@ -2910,7 +2973,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->i_security; + 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, @@ -2980,7 +3045,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(); @@ -3057,7 +3122,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; @@ -3076,7 +3141,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; } @@ -3110,12 +3175,12 @@ 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; 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; @@ -3154,7 +3219,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; @@ -3170,7 +3235,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; } @@ -3182,9 +3247,9 @@ 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; + struct inode_security_struct *isec = inode_security_novalidate(inode); *secid = isec->sid; } @@ -3207,13 +3272,14 @@ 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; 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. */ @@ -3242,7 +3308,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; @@ -3506,7 +3572,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 @@ -3624,7 +3690,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; @@ -3748,7 +3814,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 */ @@ -4065,7 +4131,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_novalidate(SOCK_INODE(sock)); struct sk_security_struct *sksec; int err = 0; @@ -4079,7 +4145,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; @@ -4265,12 +4331,12 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) if (err) return err; - newisec = SOCK_INODE(newsock)->i_security; + newisec = inode_security_novalidate(SOCK_INODE(newsock)); - isec = SOCK_INODE(sock)->i_security; + isec = inode_security_novalidate(SOCK_INODE(sock)); newisec->sclass = isec->sclass; newisec->sid = isec->sid; - newisec->initialized = 1; + newisec->initialized = LABEL_INITIALIZED; return 0; } @@ -4605,7 +4671,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 = SOCK_INODE(parent)->i_security; + 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 || @@ -4785,11 +4852,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; } @@ -4933,7 +5001,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, int ifindex, u16 family) { - struct sock *sk = skb->sk; + struct sock *sk = skb_to_full_sk(skb); struct sk_security_struct *sksec; struct common_audit_data ad; struct lsm_network_audit net = {0,}; @@ -4988,7 +5056,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, if (!secmark_active && !peerlbl_active) return NF_ACCEPT; - sk = skb->sk; + sk = skb_to_full_sk(skb); #ifdef CONFIG_XFRM /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec @@ -5033,8 +5101,6 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, u32 skb_sid; struct sk_security_struct *sksec; - if (sk->sk_state == TCP_NEW_SYN_RECV) - sk = inet_reqsk(sk)->rsk_listener; sksec = sk->sk_security; if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) return NF_DROP; @@ -5764,6 +5830,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 */ @@ -5995,6 +6070,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/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/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 { 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/netlabel.c b/security/selinux/netlabel.c index 0364120d1ec8..1f989a539fd4 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -245,7 +245,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, /* if this is a locally generated packet check to see if it is already * being labeled by it's parent socket, if it is just exit */ - sk = skb->sk; + sk = skb_to_full_sk(skb); if (sk != NULL) { struct sk_security_struct *sksec = sk->sk_security; if (sksec->nlbl_state != NLBL_REQSKB) diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 2bbb41822d8e..8495b9368190 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -83,6 +83,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] = { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, { SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, + { SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE }, }; static struct nlmsg_perm nlmsg_xfrm_perms[] = diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index c02da25d7b63..1b1fd27de632 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 */ }; @@ -147,23 +148,16 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, ssize_t length; int new_value; - length = -ENOMEM; if (count >= PAGE_SIZE) - goto out; + return -ENOMEM; /* No partial writes. */ - length = -EINVAL; if (*ppos != 0) - goto out; - - length = -ENOMEM; - page = (char *)get_zeroed_page(GFP_KERNEL); - if (!page) - goto out; + return -EINVAL; - length = -EFAULT; - if (copy_from_user(page, buf, count)) - goto out; + page = memdup_user_nul(buf, count); + if (IS_ERR(page)) + return PTR_ERR(page); length = -EINVAL; if (sscanf(page, "%d", &new_value) != 1) @@ -186,7 +180,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, } length = count; out: - free_page((unsigned long) page); + kfree(page); return length; } #else @@ -275,27 +269,20 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char *page = NULL; + char *page; ssize_t length; int new_value; - length = -ENOMEM; if (count >= PAGE_SIZE) - goto out; + return -ENOMEM; /* No partial writes. */ - length = -EINVAL; if (*ppos != 0) - goto out; - - length = -ENOMEM; - page = (char *)get_zeroed_page(GFP_KERNEL); - if (!page) - goto out; + return -EINVAL; - length = -EFAULT; - if (copy_from_user(page, buf, count)) - goto out; + page = memdup_user_nul(buf, count); + if (IS_ERR(page)) + return PTR_ERR(page); length = -EINVAL; if (sscanf(page, "%d", &new_value) != 1) @@ -313,7 +300,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, length = count; out: - free_page((unsigned long) page); + kfree(page); return length; } #else @@ -393,9 +380,9 @@ static int sel_open_policy(struct inode *inode, struct file *filp) goto err; if (i_size_read(inode) != security_policydb_len()) { - mutex_lock(&inode->i_mutex); + inode_lock(inode); i_size_write(inode, security_policydb_len()); - mutex_unlock(&inode->i_mutex); + inode_unlock(inode); } rc = security_read_policy(&plm->data, &plm->len); @@ -611,31 +598,24 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char *page = NULL; + char *page; ssize_t length; unsigned int new_value; length = task_has_security(current, SECURITY__SETCHECKREQPROT); if (length) - goto out; + return length; - length = -ENOMEM; if (count >= PAGE_SIZE) - goto out; + return -ENOMEM; /* No partial writes. */ - length = -EINVAL; if (*ppos != 0) - goto out; - - length = -ENOMEM; - page = (char *)get_zeroed_page(GFP_KERNEL); - if (!page) - goto out; + return -EINVAL; - length = -EFAULT; - if (copy_from_user(page, buf, count)) - goto out; + page = memdup_user_nul(buf, count); + if (IS_ERR(page)) + return PTR_ERR(page); length = -EINVAL; if (sscanf(page, "%u", &new_value) != 1) @@ -644,7 +624,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, selinux_checkreqprot = new_value ? 1 : 0; length = count; out: - free_page((unsigned long) page); + kfree(page); return length; } static const struct file_operations sel_checkreqprot_ops = { @@ -653,6 +633,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 */ @@ -1100,14 +1157,12 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, if (*ppos != 0) goto out; - length = -ENOMEM; - page = (char *)get_zeroed_page(GFP_KERNEL); - if (!page) - goto out; - - length = -EFAULT; - if (copy_from_user(page, buf, count)) + page = memdup_user_nul(buf, count); + if (IS_ERR(page)) { + length = PTR_ERR(page); + page = NULL; goto out; + } length = -EINVAL; if (sscanf(page, "%d", &new_value) != 1) @@ -1121,7 +1176,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf, out: mutex_unlock(&sel_mutex); - free_page((unsigned long) page); + kfree(page); return length; } @@ -1154,14 +1209,12 @@ static ssize_t sel_commit_bools_write(struct file *filep, if (*ppos != 0) goto out; - length = -ENOMEM; - page = (char *)get_zeroed_page(GFP_KERNEL); - if (!page) - goto out; - - length = -EFAULT; - if (copy_from_user(page, buf, count)) + page = memdup_user_nul(buf, count); + if (IS_ERR(page)) { + length = PTR_ERR(page); + page = NULL; goto out; + } length = -EINVAL; if (sscanf(page, "%d", &new_value) != 1) @@ -1176,7 +1229,7 @@ static ssize_t sel_commit_bools_write(struct file *filep, out: mutex_unlock(&sel_mutex); - free_page((unsigned long) page); + kfree(page); return length; } @@ -1292,31 +1345,24 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, size_t count, loff_t *ppos) { - char *page = NULL; + char *page; ssize_t ret; int new_value; ret = task_has_security(current, SECURITY__SETSECPARAM); if (ret) - goto out; + return ret; - ret = -ENOMEM; if (count >= PAGE_SIZE) - goto out; + return -ENOMEM; /* No partial writes. */ - ret = -EINVAL; if (*ppos != 0) - goto out; - - ret = -ENOMEM; - page = (char *)get_zeroed_page(GFP_KERNEL); - if (!page) - goto out; + return -EINVAL; - ret = -EFAULT; - if (copy_from_user(page, buf, count)) - goto out; + page = memdup_user_nul(buf, count); + if (IS_ERR(page)) + return PTR_ERR(page); ret = -EINVAL; if (sscanf(page, "%u", &new_value) != 1) @@ -1326,7 +1372,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, ret = count; out: - free_page((unsigned long)page); + kfree(page); return ret; } @@ -1759,6 +1805,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/conditional.c b/security/selinux/ss/conditional.c index 18643bf9894d..456e1a9bcfde 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -638,7 +638,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, { struct avtab_node *node; - if (!ctab || !key || !avd || !xperms) + if (!ctab || !key || !avd) return; for (node = avtab_search_node(ctab, key); node; @@ -657,7 +657,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) avd->auditallow |= node->datum.u.data; - if ((node->key.specified & AVTAB_ENABLED) && + if (xperms && (node->key.specified & AVTAB_ENABLED) && (node->key.specified & AVTAB_XPERMS)) services_compute_xperms_drivers(xperms, node); } 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. diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ff81026f6ddb..11f79013ae1f 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -398,12 +398,10 @@ static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead, */ static inline unsigned int smk_ptrace_mode(unsigned int mode) { - switch (mode) { - case PTRACE_MODE_READ: - return MAY_READ; - case PTRACE_MODE_ATTACH: + if (mode & PTRACE_MODE_ATTACH) return MAY_READWRITE; - } + if (mode & PTRACE_MODE_READ) + return MAY_READ; return 0; } @@ -1444,9 +1442,13 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) * Don't do anything special for these. * XATTR_NAME_SMACKIPIN * XATTR_NAME_SMACKIPOUT - * XATTR_NAME_SMACKEXEC */ - if (strcmp(name, XATTR_NAME_SMACK) == 0) + if (strcmp(name, XATTR_NAME_SMACK) == 0) { + struct super_block *sbp = d_backing_inode(dentry)->i_sb; + struct superblock_smack *sbsp = sbp->s_security; + + isp->smk_inode = sbsp->smk_default; + } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) isp->smk_task = NULL; else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) isp->smk_mmap = NULL; @@ -1465,7 +1467,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) { @@ -1519,8 +1521,6 @@ static int smack_inode_getsecurity(const struct inode *inode, * @inode: the object * @buffer: where they go * @buffer_size: size of buffer - * - * Returns 0 on success, -EINVAL otherwise */ static int smack_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) @@ -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; @@ -1549,12 +1549,8 @@ static void smack_inode_getsecid(const struct inode *inode, u32 *secid) * File Hooks */ -/** - * smack_file_permission - Smack check on file operations - * @file: unused - * @mask: unused - * - * Returns 0 +/* + * There is no smack_file_permission hook * * Should access checks be done on each read or write? * UNICOS and SELinux say yes. @@ -1563,10 +1559,6 @@ static void smack_inode_getsecid(const struct inode *inode, u32 *secid) * I'll say no for now. Smack does not do the frequent * label changing that SELinux does. */ -static int smack_file_permission(struct file *file, int mask) -{ - return 0; -} /** * smack_file_alloc_security - assign a file security blob @@ -1860,12 +1852,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. */ @@ -3758,7 +3772,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; @@ -4485,16 +4499,10 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, return 0; } -/** - * smack_audit_rule_free - free smack rule representation - * @vrule: rule to be freed. - * +/* + * There is no need for a smack_audit_rule_free hook. * No memory was allocated. */ -static void smack_audit_rule_free(void *vrule) -{ - /* No-op */ -} #endif /* CONFIG_AUDIT */ @@ -4545,16 +4553,11 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) return 0; } -/** - * smack_release_secctx - don't do anything. - * @secdata: unused - * @seclen: unused - * - * Exists to make sure nothing gets done, and properly +/* + * There used to be a smack_release_secctx hook + * that did nothing back when hooks were in a vector. + * Now that there's a list such a hook adds cost. */ -static void smack_release_secctx(char *secdata, u32 seclen) -{ -} static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) { @@ -4613,7 +4616,6 @@ static struct security_hook_list smack_hooks[] = { LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity), LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid), - LSM_HOOK_INIT(file_permission, smack_file_permission), LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security), LSM_HOOK_INIT(file_free_security, smack_file_free_security), LSM_HOOK_INIT(file_ioctl, smack_file_ioctl), @@ -4708,13 +4710,11 @@ static struct security_hook_list smack_hooks[] = { LSM_HOOK_INIT(audit_rule_init, smack_audit_rule_init), LSM_HOOK_INIT(audit_rule_known, smack_audit_rule_known), LSM_HOOK_INIT(audit_rule_match, smack_audit_rule_match), - LSM_HOOK_INIT(audit_rule_free, smack_audit_rule_free), #endif /* CONFIG_AUDIT */ LSM_HOOK_INIT(ismaclabel, smack_ismaclabel), LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx), LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid), - LSM_HOOK_INIT(release_secctx, smack_release_secctx), LSM_HOOK_INIT(inode_notifysecctx, smack_inode_notifysecctx), LSM_HOOK_INIT(inode_setsecctx, smack_inode_setsecctx), LSM_HOOK_INIT(inode_getsecctx, smack_inode_getsecctx), diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c index 6d1706c9777e..aa6bf1b22ec5 100644 --- a/security/smack/smack_netfilter.c +++ b/security/smack/smack_netfilter.c @@ -17,6 +17,7 @@ #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> #include <linux/netdevice.h> +#include <net/inet_sock.h> #include "smack.h" #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -25,11 +26,12 @@ static unsigned int smack_ipv6_output(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { + struct sock *sk = skb_to_full_sk(skb); struct socket_smack *ssp; struct smack_known *skp; - if (skb && skb->sk && skb->sk->sk_security) { - ssp = skb->sk->sk_security; + if (sk && sk->sk_security) { + ssp = sk->sk_security; skp = ssp->smk_out; skb->secmark = skp->smk_secid; } @@ -42,11 +44,12 @@ static unsigned int smack_ipv4_output(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { + struct sock *sk = skb_to_full_sk(skb); struct socket_smack *ssp; struct smack_known *skp; - if (skb && skb->sk && skb->sk->sk_security) { - ssp = skb->sk->sk_security; + if (sk && sk->sk_security) { + ssp = sk->sk_security; skp = ssp->smk_out; skb->secmark = skp->smk_secid; } diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 94bd9e41c9ec..e249a66db533 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -497,14 +497,9 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, } } - data = kmalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - rc = -EFAULT; - goto out; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); /* * In case of parsing only part of user buf, @@ -884,16 +879,10 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX)) return -EINVAL; - data = kzalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - rc = -EFAULT; - goto unlockedout; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); - data[count] = '\0'; rule = data; /* * Only allow one writer at a time. Writes should be @@ -946,7 +935,6 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, out: mutex_unlock(&smack_cipso_lock); -unlockedout: kfree(data); return rc; } @@ -1187,14 +1175,9 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf, if (count < SMK_NETLBLADDRMIN) return -EINVAL; - data = kzalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - rc = -EFAULT; - goto free_data_out; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); smack = kzalloc(count + 1, GFP_KERNEL); if (smack == NULL) { @@ -1202,8 +1185,6 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf, goto free_data_out; } - data[count] = '\0'; - rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s", &host[0], &host[1], &host[2], &host[3], &masks, smack); if (rc != 6) { @@ -1454,14 +1435,9 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, if (count < SMK_NETLBLADDRMIN) return -EINVAL; - data = kzalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - rc = -EFAULT; - goto free_data_out; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); smack = kzalloc(count + 1, GFP_KERNEL); if (smack == NULL) { @@ -1469,8 +1445,6 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, goto free_data_out; } - data[count] = '\0'; - i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x/%u %s", &scanned[0], &scanned[1], &scanned[2], &scanned[3], &scanned[4], &scanned[5], &scanned[6], &scanned[7], @@ -1865,14 +1839,9 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - data = kzalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - rc = -EFAULT; - goto out; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); skp = smk_import_entry(data, count); if (IS_ERR(skp)) { @@ -2041,14 +2010,9 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - data = kzalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - kfree(data); - return -EFAULT; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); rc = smk_parse_label_list(data, &list_tmp); kfree(data); @@ -2133,14 +2097,9 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf, if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - data = kzalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - rc = -EFAULT; - goto freeout; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); /* * Clear the smack_unconfined on invalid label errors. This means @@ -2696,19 +2655,15 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - data = kzalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); - if (copy_from_user(data, buf, count) != 0) - rc = -EFAULT; - else { - skp = smk_import_entry(data, count); - if (IS_ERR(skp)) - rc = PTR_ERR(skp); - else - smack_syslog_label = skp; - } + skp = smk_import_entry(data, count); + if (IS_ERR(skp)) + rc = PTR_ERR(skp); + else + smack_syslog_label = skp; kfree(data); return rc; @@ -2798,14 +2753,9 @@ static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf, if (*ppos != 0) return -EINVAL; - data = kzalloc(count + 1, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - kfree(data); - return -EFAULT; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); rc = smk_parse_label_list(data, &list_tmp); kfree(data); diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 38651454ed08..ade7c6cad172 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -874,7 +874,14 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, } /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */ #ifdef CONFIG_MMU - if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) + /* + * This is called at execve() time in order to dig around + * in the argv/environment of the new proceess + * (represented by bprm). 'current' is the process doing + * the execve(). + */ + if (get_user_pages_remote(current, bprm->mm, pos, 1, + 0, 1, &page, NULL) <= 0) return false; #else page = bprm->page[pos / PAGE_SIZE]; diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 179a955b319d..06ab41b1ff28 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -43,13 +43,9 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, int error; if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10) return -ENOMEM; - data = kzalloc(count + 1, GFP_NOFS); - if (!data) - return -ENOMEM; - if (copy_from_user(data, buf, count)) { - error = -EFAULT; - goto out; - } + data = memdup_user_nul(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); tomoyo_normalize_line(data); if (tomoyo_correct_domain(data)) { const int idx = tomoyo_read_lock(); @@ -87,7 +83,6 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, tomoyo_read_unlock(idx); } else error = -EINVAL; -out: kfree(data); return error ? error : count; } diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index d3c19c970a06..cb6ed10816d4 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -281,7 +281,7 @@ static int yama_ptrace_access_check(struct task_struct *child, int rc = 0; /* require ptrace target be a child of ptracer on attach */ - if (mode == PTRACE_MODE_ATTACH) { + if (mode & PTRACE_MODE_ATTACH) { switch (ptrace_scope) { case YAMA_SCOPE_DISABLED: /* No additional restrictions. */ @@ -307,7 +307,7 @@ static int yama_ptrace_access_check(struct task_struct *child, } } - if (rc) { + if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) { printk_ratelimited(KERN_NOTICE "ptrace of pid %d was attempted by: %s (pid %d)\n", child->pid, current->comm, current->pid); |