diff options
Diffstat (limited to 'security')
48 files changed, 1445 insertions, 702 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 452567d3a08e..d97cba3e3849 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -621,7 +621,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) * There is no exception for unconfined as change_hat is not * available. */ - if (current->no_new_privs) + if (task_no_new_privs(current)) return -EPERM; /* released below */ @@ -776,7 +776,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, * no_new_privs is set because this aways results in a reduction * of permissions. */ - if (current->no_new_privs && !unconfined(profile)) { + if (task_no_new_privs(current) && !unconfined(profile)) { put_cred(cred); return -EPERM; } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 998100093332..65ca451a764d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -668,7 +668,7 @@ static int param_set_aabool(const char *val, const struct kernel_param *kp); static int param_get_aabool(char *buffer, const struct kernel_param *kp); #define param_check_aabool param_check_bool static struct kernel_param_ops param_ops_aabool = { - .flags = KERNEL_PARAM_FL_NOARG, + .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_aabool, .get = param_get_aabool }; @@ -685,7 +685,7 @@ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp); #define param_check_aalockpolicy param_check_bool static struct kernel_param_ops param_ops_aalockpolicy = { - .flags = KERNEL_PARAM_FL_NOARG, + .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_aalockpolicy, .get = param_get_aalockpolicy }; diff --git a/security/capability.c b/security/capability.c index e76373de3129..d68c57a62bcf 100644 --- a/security/capability.c +++ b/security/capability.c @@ -343,9 +343,9 @@ static int cap_file_fcntl(struct file *file, unsigned int cmd, return 0; } -static int cap_file_set_fowner(struct file *file) +static void cap_file_set_fowner(struct file *file) { - return 0; + return; } static int cap_file_send_sigiotask(struct task_struct *tsk, @@ -401,6 +401,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode) return 0; } +static int cap_kernel_fw_from_file(struct file *file, char *buf, size_t size) +{ + return 0; +} + static int cap_kernel_module_request(char *kmod_name) { return 0; @@ -1015,6 +1020,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, cred_transfer); set_to_cap_if_null(ops, kernel_act_as); set_to_cap_if_null(ops, kernel_create_files_as); + set_to_cap_if_null(ops, kernel_fw_from_file); set_to_cap_if_null(ops, kernel_module_request); set_to_cap_if_null(ops, kernel_module_from_file); set_to_cap_if_null(ops, task_fix_setuid); diff --git a/security/commoncap.c b/security/commoncap.c index b9d613e0ef14..bab0611afc1e 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -421,6 +421,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable); } + cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + return 0; } @@ -822,15 +825,20 @@ int cap_task_setnice(struct task_struct *p, int nice) * Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from * the current task's bounding set. Returns 0 on success, -ve on error. */ -static long cap_prctl_drop(struct cred *new, unsigned long cap) +static int cap_prctl_drop(unsigned long cap) { + struct cred *new; + if (!ns_capable(current_user_ns(), CAP_SETPCAP)) return -EPERM; if (!cap_valid(cap)) return -EINVAL; + new = prepare_creds(); + if (!new) + return -ENOMEM; cap_lower(new->cap_bset, cap); - return 0; + return commit_creds(new); } /** @@ -848,26 +856,17 @@ static long cap_prctl_drop(struct cred *new, unsigned long cap) int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { + const struct cred *old = current_cred(); struct cred *new; - long error = 0; - - new = prepare_creds(); - if (!new) - return -ENOMEM; switch (option) { case PR_CAPBSET_READ: - error = -EINVAL; if (!cap_valid(arg2)) - goto error; - error = !!cap_raised(new->cap_bset, arg2); - goto no_change; + return -EINVAL; + return !!cap_raised(old->cap_bset, arg2); case PR_CAPBSET_DROP: - error = cap_prctl_drop(new, arg2); - if (error < 0) - goto error; - goto changed; + return cap_prctl_drop(arg2); /* * The next four prctl's remain to assist with transitioning a @@ -889,10 +888,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, * capability-based-privilege environment. */ case PR_SET_SECUREBITS: - error = -EPERM; - if ((((new->securebits & SECURE_ALL_LOCKS) >> 1) - & (new->securebits ^ arg2)) /*[1]*/ - || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ + if ((((old->securebits & SECURE_ALL_LOCKS) >> 1) + & (old->securebits ^ arg2)) /*[1]*/ + || ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ || (cap_capable(current_cred(), current_cred()->user_ns, CAP_SETPCAP, @@ -906,46 +904,39 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, */ ) /* cannot change a locked bit */ - goto error; + return -EPERM; + + new = prepare_creds(); + if (!new) + return -ENOMEM; new->securebits = arg2; - goto changed; + return commit_creds(new); case PR_GET_SECUREBITS: - error = new->securebits; - goto no_change; + return old->securebits; case PR_GET_KEEPCAPS: - if (issecure(SECURE_KEEP_CAPS)) - error = 1; - goto no_change; + return !!issecure(SECURE_KEEP_CAPS); case PR_SET_KEEPCAPS: - error = -EINVAL; if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */ - goto error; - error = -EPERM; + return -EINVAL; if (issecure(SECURE_KEEP_CAPS_LOCKED)) - goto error; + return -EPERM; + + new = prepare_creds(); + if (!new) + return -ENOMEM; if (arg2) new->securebits |= issecure_mask(SECURE_KEEP_CAPS); else new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); - goto changed; + return commit_creds(new); default: /* No functionality available - continue with default */ - error = -ENOSYS; - goto error; + return -ENOSYS; } - - /* Functionality provided */ -changed: - return commit_creds(new); - -no_change: -error: - abort_creds(new); - return error; } /** diff --git a/security/device_cgroup.c b/security/device_cgroup.c index d9d69e6930ed..188c1d26393b 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -796,7 +796,7 @@ struct cgroup_subsys devices_cgrp_subsys = { .css_free = devcgroup_css_free, .css_online = devcgroup_online, .css_offline = devcgroup_offline, - .base_cftypes = dev_cgroup_files, + .legacy_cftypes = dev_cgroup_files, }; /** diff --git a/security/inode.c b/security/inode.c index 43ce6e19015f..8e7ca62078ab 100644 --- a/security/inode.c +++ b/security/inode.c @@ -74,7 +74,7 @@ static struct file_system_type fs_type = { * pointer must be passed to the securityfs_remove() function when the file is * to be removed (no automatic cleanup happens if your module is unloaded, * you are responsible here). If an error occurs, the function will return - * the erorr value (via ERR_PTR). + * the error value (via ERR_PTR). * * If securityfs is not enabled in the kernel, the value %-ENODEV is * returned. diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 245c6d92065b..b76235ae4786 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -1,11 +1,23 @@ # config INTEGRITY - def_bool y - depends on IMA || EVM + bool "Integrity subsystem" + depends on SECURITY + default y + help + This option enables the integrity subsystem, which is comprised + of a number of different components including the Integrity + Measurement Architecture (IMA), Extended Verification Module + (EVM), IMA-appraisal extension, digital signature verification + extension and audit measurement log support. + + Each of these components can be enabled/disabled separately. + Refer to the individual components for additional details. + +if INTEGRITY config INTEGRITY_SIGNATURE boolean "Digital signature verification using multiple keyrings" - depends on INTEGRITY && KEYS + depends on KEYS default n select SIGNATURE help @@ -17,9 +29,21 @@ config INTEGRITY_SIGNATURE This is useful for evm and module keyrings, when keys are usually only added from initramfs. +config INTEGRITY_ASYMMETRIC_KEYS + boolean "Enable asymmetric keys support" + depends on INTEGRITY_SIGNATURE + default n + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select PUBLIC_KEY_ALGO_RSA + select X509_CERTIFICATE_PARSER + help + This option enables digital signature verification using + asymmetric keys. + config INTEGRITY_AUDIT bool "Enables integrity auditing support " - depends on INTEGRITY && AUDIT + depends on AUDIT default y help In addition to enabling integrity auditing support, this @@ -32,17 +56,7 @@ config INTEGRITY_AUDIT be enabled by specifying 'integrity_audit=1' on the kernel command line. -config INTEGRITY_ASYMMETRIC_KEYS - boolean "Enable asymmetric keys support" - depends on INTEGRITY_SIGNATURE - default n - select ASYMMETRIC_KEY_TYPE - select ASYMMETRIC_PUBLIC_KEY_SUBTYPE - select PUBLIC_KEY_ALGO_RSA - select X509_CERTIFICATE_PARSER - help - This option enables digital signature verification using - asymmetric keys. - source security/integrity/ima/Kconfig source security/integrity/evm/Kconfig + +endif # if INTEGRITY diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 0793f4811cb7..8d1f4bf51087 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -3,11 +3,11 @@ # obj-$(CONFIG_INTEGRITY) += integrity.o -obj-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o -obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o -obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o integrity-y := iint.o +integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o +integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o +integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o subdir-$(CONFIG_IMA) += ima obj-$(CONFIG_IMA) += ima/ diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index b4af4ebc5be2..8d4fbff8b87c 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -13,7 +13,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/err.h> +#include <linux/sched.h> #include <linux/rbtree.h> +#include <linux/cred.h> #include <linux/key-type.h> #include <linux/digsig.h> @@ -24,7 +26,11 @@ static struct key *keyring[INTEGRITY_KEYRING_MAX]; static const char *keyring_name[INTEGRITY_KEYRING_MAX] = { "_evm", "_module", +#ifndef CONFIG_IMA_TRUSTED_KEYRING "_ima", +#else + ".ima", +#endif }; int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, @@ -56,3 +62,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, return -EOPNOTSUPP; } + +int integrity_init_keyring(const unsigned int id) +{ + const struct cred *cred = current_cred(); + int err = 0; + + keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), + KGIDT_INIT(0), 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(keyring[id])) + set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags); + else { + err = PTR_ERR(keyring[id]); + pr_info("Can't allocate %s keyring (%d)\n", + keyring_name[id], err); + keyring[id] = NULL; + } + return err; +} diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 9eae4809006b..4fec1816a2b3 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/err.h> +#include <linux/ratelimit.h> #include <linux/key-type.h> #include <crypto/public_key.h> #include <keys/asymmetric-type.h> @@ -27,7 +28,7 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) struct key *key; char name[12]; - sprintf(name, "id:%x", keyid); + sprintf(name, "id:%08x", keyid); pr_debug("key search: \"%s\"\n", name); @@ -45,8 +46,8 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) } if (IS_ERR(key)) { - pr_warn("Request for unknown key '%s' err %ld\n", - name, PTR_ERR(key)); + pr_err_ratelimited("Request for unknown key '%s' err %ld\n", + name, PTR_ERR(key)); switch (PTR_ERR(key)) { /* Hide some search errors */ case -EACCES: diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index d606f3d12d6b..df586fa00ef1 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -1,6 +1,5 @@ config EVM boolean "EVM support" - depends on SECURITY select KEYS select ENCRYPTED_KEYS select CRYPTO_HMAC @@ -12,10 +11,6 @@ config EVM If you are unsure how to answer this question, answer N. -if EVM - -menu "EVM options" - config EVM_ATTR_FSUUID bool "FSUUID (version 2)" default y @@ -47,6 +42,3 @@ config EVM_EXTRA_SMACK_XATTRS additional info to the calculation, requires existing EVM labeled file systems to be relabeled. -endmenu - -endif diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 3bcb80df4d01..c5ee1a7c5e8a 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -126,14 +126,15 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, GFP_NOFS); if (rc <= 0) { - if (rc == 0) - evm_status = INTEGRITY_FAIL; /* empty */ - else if (rc == -ENODATA) { + evm_status = INTEGRITY_FAIL; + if (rc == -ENODATA) { rc = evm_find_protected_xattrs(dentry); if (rc > 0) evm_status = INTEGRITY_NOLABEL; else if (rc == 0) evm_status = INTEGRITY_NOXATTRS; /* new file */ + } else if (rc == -EOPNOTSUPP) { + evm_status = INTEGRITY_UNKNOWN; } goto out; } @@ -284,6 +285,13 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, goto out; } evm_status = evm_verify_current_integrity(dentry); + if (evm_status == INTEGRITY_NOXATTRS) { + struct integrity_iint_cache *iint; + + iint = integrity_iint_find(dentry->d_inode); + if (iint && (iint->flags & IMA_NEW_FILE)) + return 0; + } out: if (evm_status != INTEGRITY_PASS) integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode, @@ -311,9 +319,12 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, { const struct evm_ima_xattr_data *xattr_data = xattr_value; - if ((strcmp(xattr_name, XATTR_NAME_EVM) == 0) - && (xattr_data->type == EVM_XATTR_HMAC)) - return -EPERM; + if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) { + if (!xattr_value_len) + return -EINVAL; + if (xattr_data->type != EVM_IMA_XATTR_DIGSIG) + return -EPERM; + } return evm_protect_xattr(dentry, xattr_name, xattr_value, xattr_value_len); } @@ -352,7 +363,6 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, return; evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); - return; } /** @@ -372,7 +382,6 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) mutex_lock(&inode->i_mutex); evm_update_evmxattr(dentry, xattr_name, NULL, 0); mutex_unlock(&inode->i_mutex); - return; } /** @@ -414,7 +423,6 @@ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) evm_update_evmxattr(dentry, NULL, NULL, 0); - return; } /* diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 81a27971d884..e099875643c5 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -2,8 +2,6 @@ # config IMA bool "Integrity Measurement Architecture(IMA)" - depends on SECURITY - select INTEGRITY select SECURITYFS select CRYPTO select CRYPTO_HMAC @@ -123,3 +121,13 @@ config IMA_APPRAISE For more information on integrity appraisal refer to: <http://linux-ima.sourceforge.net> If unsure, say N. + +config IMA_TRUSTED_KEYRING + bool "Require all keys on the .ima keyring be signed" + depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING + depends on INTEGRITY_ASYMMETRIC_KEYS + select KEYS_DEBUG_PROC_KEYS + default y + help + This option requires that all keys added to the .ima + keyring be signed by a key on the system trusted keyring. diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index f79fa8be203c..8ee997dff139 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -43,6 +43,9 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; #define IMA_TEMPLATE_IMA_NAME "ima" #define IMA_TEMPLATE_IMA_FMT "d|n" +/* current content of the policy */ +extern int ima_policy_flag; + /* set during initialization */ extern int ima_initialized; extern int ima_used_chip; @@ -90,10 +93,7 @@ extern struct list_head ima_measurements; /* list of all measurements */ /* Internal IMA function definitions */ int ima_init(void); -void ima_cleanup(void); int ima_fs_init(void); -void ima_fs_cleanup(void); -int ima_inode_alloc(struct inode *inode); int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, const unsigned char *filename); @@ -110,8 +110,6 @@ void ima_print_digest(struct seq_file *m, u8 *digest, int size); struct ima_template_desc *ima_template_desc_current(void); int ima_init_template(void); -int ima_init_template(void); - /* * used to protect h_table and sha_table */ @@ -151,32 +149,29 @@ int ima_store_template(struct ima_template_entry *entry, int violation, void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(struct path *path, char **pathbuf); -/* rbtree tree calls to lookup, insert, delete - * integrity data associated with an inode. - */ -struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); -struct integrity_iint_cache *integrity_iint_find(struct inode *inode); - /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR }; +enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR }; int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); void ima_init_policy(void); void ima_update_policy(void); +void ima_update_policy_flag(void); ssize_t ima_parse_add_rule(char *); void ima_delete_rules(void); /* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 #define IMA_APPRAISE_FIX 0x02 -#define IMA_APPRAISE_MODULES 0x04 +#define IMA_APPRAISE_LOG 0x04 +#define IMA_APPRAISE_MODULES 0x08 +#define IMA_APPRAISE_FIRMWARE 0x10 #ifdef CONFIG_IMA_APPRAISE int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len); + 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, @@ -192,7 +187,7 @@ static inline int ima_appraise_measurement(int func, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len) + int xattr_len, int opened) { return INTEGRITY_UNKNOWN; } @@ -249,4 +244,16 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, return -EINVAL; } #endif /* CONFIG_IMA_LSM_RULES */ + +#ifdef CONFIG_IMA_TRUSTED_KEYRING +static inline int ima_init_keyring(const unsigned int id) +{ + return integrity_init_keyring(id); +} +#else +static inline int ima_init_keyring(const unsigned int id) +{ + return 0; +} +#endif /* CONFIG_IMA_TRUSTED_KEYRING */ #endif diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index d9cd5ce14d2b..86885979918c 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -179,11 +179,6 @@ int ima_get_action(struct inode *inode, int mask, int function) return ima_match_policy(inode, function, mask, flags); } -int ima_must_measure(struct inode *inode, int mask, int function) -{ - return ima_match_policy(inode, function, mask, IMA_MEASURE); -} - /* * ima_collect_measurement - collect file measurement * @@ -330,10 +325,9 @@ const char *ima_d_path(struct path *path, char **pathbuf) { char *pathname = NULL; - /* We will allow 11 spaces for ' (deleted)' to be appended */ - *pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL); + *pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); if (*pathbuf) { - pathname = d_path(path, *pathbuf, PATH_MAX + 11); + pathname = d_absolute_path(path, *pathbuf, PATH_MAX); if (IS_ERR(pathname)) { kfree(*pathbuf); *pathbuf = NULL; diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index d3113d4aaa3c..7c8f41e618b6 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -23,6 +23,8 @@ static int __init default_appraise_setup(char *str) { if (strncmp(str, "off", 3) == 0) ima_appraise = 0; + else if (strncmp(str, "log", 3) == 0) + ima_appraise = IMA_APPRAISE_LOG; else if (strncmp(str, "fix", 3) == 0) ima_appraise = IMA_APPRAISE_FIX; return 1; @@ -75,6 +77,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, return iint->ima_bprm_status; case MODULE_CHECK: return iint->ima_module_status; + case FIRMWARE_CHECK: + return iint->ima_firmware_status; case FILE_CHECK: default: return iint->ima_file_status; @@ -94,6 +98,9 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint, case MODULE_CHECK: iint->ima_module_status = status; break; + case FIRMWARE_CHECK: + iint->ima_firmware_status = status; + break; case FILE_CHECK: default: iint->ima_file_status = status; @@ -113,6 +120,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func) case MODULE_CHECK: iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); break; + case FIRMWARE_CHECK: + iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED); + break; case FILE_CHECK: default: iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); @@ -175,7 +185,7 @@ int ima_read_xattr(struct dentry *dentry, int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len) + int xattr_len, int opened) { static const char op[] = "appraise_data"; char *cause = "unknown"; @@ -184,8 +194,6 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, enum integrity_status status = INTEGRITY_UNKNOWN; int rc = xattr_len, hash_start = 0; - if (!ima_appraise) - return 0; if (!inode->i_op->getxattr) return INTEGRITY_UNKNOWN; @@ -194,8 +202,11 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, goto out; cause = "missing-hash"; - status = - (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL; + status = INTEGRITY_NOLABEL; + if (opened & FILE_CREATED) { + iint->flags |= IMA_NEW_FILE; + status = INTEGRITY_PASS; + } goto out; } @@ -214,7 +225,7 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, hash_start = 1; case IMA_XATTR_DIGEST: if (iint->flags & IMA_DIGSIG_REQUIRED) { - cause = "IMA signature required"; + cause = "IMA-signature-required"; status = INTEGRITY_FAIL; break; } @@ -307,7 +318,7 @@ void ima_inode_post_setattr(struct dentry *dentry) struct integrity_iint_cache *iint; int must_appraise, rc; - if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode) + if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode) || !inode->i_op->removexattr) return; @@ -345,7 +356,7 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig) { struct integrity_iint_cache *iint; - if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)) + if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)) return; iint = integrity_iint_find(inode); @@ -367,6 +378,8 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, result = ima_protect_xattr(dentry, xattr_name, xattr_value, xattr_value_len); if (result == 1) { + if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST)) + return -EINVAL; ima_reset_appraise_flags(dentry->d_inode, (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0); result = 0; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index ccd0ac8fa9a0..78d66dae15f4 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -16,6 +16,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/ratelimit.h> #include <linux/file.h> #include <linux/crypto.h> #include <linux/scatterlist.h> @@ -25,7 +27,45 @@ #include <crypto/hash_info.h> #include "ima.h" +struct ahash_completion { + struct completion completion; + int err; +}; + +/* minimum file size for ahash use */ +static unsigned long ima_ahash_minsize; +module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644); +MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use"); + +/* default is 0 - 1 page. */ +static int ima_maxorder; +static unsigned int ima_bufsize = PAGE_SIZE; + +static int param_set_bufsize(const char *val, const struct kernel_param *kp) +{ + unsigned long long size; + int order; + + size = memparse(val, NULL); + order = get_order(size); + if (order >= MAX_ORDER) + return -EINVAL; + ima_maxorder = order; + ima_bufsize = PAGE_SIZE << order; + return 0; +} + +static struct kernel_param_ops param_ops_bufsize = { + .set = param_set_bufsize, + .get = param_get_uint, +}; +#define param_check_bufsize(name, p) __param_check(name, p, unsigned int) + +module_param_named(ahash_bufsize, ima_bufsize, bufsize, 0644); +MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size"); + static struct crypto_shash *ima_shash_tfm; +static struct crypto_ahash *ima_ahash_tfm; /** * ima_kernel_read - read file content @@ -40,24 +80,24 @@ static int ima_kernel_read(struct file *file, loff_t offset, { mm_segment_t old_fs; char __user *buf = addr; - ssize_t ret; + ssize_t ret = -EINVAL; if (!(file->f_mode & FMODE_READ)) return -EBADF; - if (!file->f_op->read && !file->f_op->aio_read) - return -EINVAL; old_fs = get_fs(); set_fs(get_ds()); if (file->f_op->read) ret = file->f_op->read(file, buf, count, &offset); - else + else if (file->f_op->aio_read) ret = do_sync_read(file, buf, count, &offset); + else if (file->f_op->read_iter) + ret = new_sync_read(file, buf, count, &offset); set_fs(old_fs); return ret; } -int ima_init_crypto(void) +int __init ima_init_crypto(void) { long rc; @@ -76,7 +116,10 @@ static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo) struct crypto_shash *tfm = ima_shash_tfm; int rc; - if (algo != ima_hash_algo && algo < HASH_ALGO__LAST) { + if (algo < 0 || algo >= HASH_ALGO__LAST) + algo = ima_hash_algo; + + if (algo != ima_hash_algo) { tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); if (IS_ERR(tfm)) { rc = PTR_ERR(tfm); @@ -93,9 +136,249 @@ static void ima_free_tfm(struct crypto_shash *tfm) crypto_free_shash(tfm); } -/* - * Calculate the MD5/SHA1 file digest +/** + * ima_alloc_pages() - Allocate contiguous pages. + * @max_size: Maximum amount of memory to allocate. + * @allocated_size: Returned size of actual allocation. + * @last_warn: Should the min_size allocation warn or not. + * + * Tries to do opportunistic allocation for memory first trying to allocate + * max_size amount of memory and then splitting that until zero order is + * reached. Allocation is tried without generating allocation warnings unless + * last_warn is set. Last_warn set affects only last allocation of zero order. + * + * By default, ima_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL) + * + * Return pointer to allocated memory, or NULL on failure. */ +static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size, + int last_warn) +{ + void *ptr; + int order = ima_maxorder; + gfp_t gfp_mask = __GFP_WAIT | __GFP_NOWARN | __GFP_NORETRY; + + if (order) + order = min(get_order(max_size), order); + + for (; order; order--) { + ptr = (void *)__get_free_pages(gfp_mask, order); + if (ptr) { + *allocated_size = PAGE_SIZE << order; + return ptr; + } + } + + /* order is zero - one page */ + + gfp_mask = GFP_KERNEL; + + if (!last_warn) + gfp_mask |= __GFP_NOWARN; + + ptr = (void *)__get_free_pages(gfp_mask, 0); + if (ptr) { + *allocated_size = PAGE_SIZE; + return ptr; + } + + *allocated_size = 0; + return NULL; +} + +/** + * ima_free_pages() - Free pages allocated by ima_alloc_pages(). + * @ptr: Pointer to allocated pages. + * @size: Size of allocated buffer. + */ +static void ima_free_pages(void *ptr, size_t size) +{ + if (!ptr) + return; + free_pages((unsigned long)ptr, get_order(size)); +} + +static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo) +{ + struct crypto_ahash *tfm = ima_ahash_tfm; + int rc; + + if (algo < 0 || algo >= HASH_ALGO__LAST) + algo = ima_hash_algo; + + if (algo != ima_hash_algo || !tfm) { + tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0); + if (!IS_ERR(tfm)) { + if (algo == ima_hash_algo) + ima_ahash_tfm = tfm; + } else { + rc = PTR_ERR(tfm); + pr_err("Can not allocate %s (reason: %d)\n", + hash_algo_name[algo], rc); + } + } + return tfm; +} + +static void ima_free_atfm(struct crypto_ahash *tfm) +{ + if (tfm != ima_ahash_tfm) + crypto_free_ahash(tfm); +} + +static void ahash_complete(struct crypto_async_request *req, int err) +{ + struct ahash_completion *res = req->data; + + if (err == -EINPROGRESS) + return; + res->err = err; + complete(&res->completion); +} + +static int ahash_wait(int err, struct ahash_completion *res) +{ + switch (err) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + wait_for_completion(&res->completion); + reinit_completion(&res->completion); + err = res->err; + /* fall through */ + default: + pr_crit_ratelimited("ahash calculation failed: err: %d\n", err); + } + + return err; +} + +static int ima_calc_file_hash_atfm(struct file *file, + struct ima_digest_data *hash, + struct crypto_ahash *tfm) +{ + loff_t i_size, offset; + char *rbuf[2] = { NULL, }; + int rc, read = 0, rbuf_len, active = 0, ahash_rc = 0; + struct ahash_request *req; + struct scatterlist sg[1]; + struct ahash_completion res; + size_t rbuf_size[2]; + + 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 out1; + + i_size = i_size_read(file_inode(file)); + + if (i_size == 0) + goto out2; + + /* + * Try to allocate maximum size of memory. + * Fail if even a single page cannot be allocated. + */ + rbuf[0] = ima_alloc_pages(i_size, &rbuf_size[0], 1); + if (!rbuf[0]) { + rc = -ENOMEM; + goto out1; + } + + /* Only allocate one buffer if that is enough. */ + if (i_size > rbuf_size[0]) { + /* + * Try to allocate secondary buffer. If that fails fallback to + * using single buffering. Use previous memory allocation size + * as baseline for possible allocation size. + */ + rbuf[1] = ima_alloc_pages(i_size - rbuf_size[0], + &rbuf_size[1], 0); + } + + if (!(file->f_mode & FMODE_READ)) { + file->f_mode |= FMODE_READ; + read = 1; + } + + for (offset = 0; offset < i_size; offset += rbuf_len) { + if (!rbuf[1] && offset) { + /* Not using two buffers, and it is not the first + * read/request, wait for the completion of the + * previous ahash_update() request. + */ + rc = ahash_wait(ahash_rc, &res); + if (rc) + goto out3; + } + /* read buffer */ + rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]); + rc = ima_kernel_read(file, offset, rbuf[active], rbuf_len); + if (rc != rbuf_len) + goto out3; + + if (rbuf[1] && offset) { + /* Using two buffers, and it is not the first + * read/request, wait for the completion of the + * previous ahash_update() request. + */ + rc = ahash_wait(ahash_rc, &res); + if (rc) + goto out3; + } + + sg_init_one(&sg[0], rbuf[active], rbuf_len); + ahash_request_set_crypt(req, sg, NULL, rbuf_len); + + ahash_rc = crypto_ahash_update(req); + + if (rbuf[1]) + active = !active; /* swap buffers, if we use two */ + } + /* wait for the last update request to complete */ + rc = ahash_wait(ahash_rc, &res); +out3: + if (read) + file->f_mode &= ~FMODE_READ; + ima_free_pages(rbuf[0], rbuf_size[0]); + ima_free_pages(rbuf[1], rbuf_size[1]); +out2: + if (!rc) { + ahash_request_set_crypt(req, NULL, hash->digest, 0); + rc = ahash_wait(crypto_ahash_final(req), &res); + } +out1: + ahash_request_free(req); + return rc; +} + +static int ima_calc_file_ahash(struct file *file, 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 = ima_calc_file_hash_atfm(file, hash, tfm); + + ima_free_atfm(tfm); + + return rc; +} + static int ima_calc_file_hash_tfm(struct file *file, struct ima_digest_data *hash, struct crypto_shash *tfm) @@ -103,17 +386,14 @@ static int ima_calc_file_hash_tfm(struct file *file, loff_t i_size, offset = 0; char *rbuf; int rc, read = 0; - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(tfm)]; - } desc; + SHASH_DESC_ON_STACK(shash, tfm); - desc.shash.tfm = tfm; - desc.shash.flags = 0; + shash->tfm = tfm; + shash->flags = 0; hash->length = crypto_shash_digestsize(tfm); - rc = crypto_shash_init(&desc.shash); + rc = crypto_shash_init(shash); if (rc != 0) return rc; @@ -143,7 +423,7 @@ static int ima_calc_file_hash_tfm(struct file *file, break; offset += rbuf_len; - rc = crypto_shash_update(&desc.shash, rbuf, rbuf_len); + rc = crypto_shash_update(shash, rbuf, rbuf_len); if (rc) break; } @@ -152,11 +432,11 @@ static int ima_calc_file_hash_tfm(struct file *file, kfree(rbuf); out: if (!rc) - rc = crypto_shash_final(&desc.shash, hash->digest); + rc = crypto_shash_final(shash, hash->digest); return rc; } -int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) +static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash) { struct crypto_shash *tfm; int rc; @@ -173,6 +453,35 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) } /* + * ima_calc_file_hash - calculate file hash + * + * Asynchronous hash (ahash) allows using HW acceleration for calculating + * a hash. ahash performance varies for different data sizes on different + * crypto accelerators. shash performance might be better for smaller files. + * The 'ima.ahash_minsize' module parameter allows specifying the best + * minimum file size for using ahash on the system. + * + * If the ima.ahash_minsize parameter is not specified, this function uses + * shash for the hash calculation. If ahash fails, it falls back to using + * shash. + */ +int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) +{ + loff_t i_size; + int rc; + + i_size = i_size_read(file_inode(file)); + + if (ima_ahash_minsize && i_size >= ima_ahash_minsize) { + rc = ima_calc_file_ahash(file, hash); + if (!rc) + return 0; + } + + return ima_calc_file_shash(file, hash); +} + +/* * Calculate the hash of template data */ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, @@ -181,18 +490,15 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, struct ima_digest_data *hash, struct crypto_shash *tfm) { - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(tfm)]; - } desc; + SHASH_DESC_ON_STACK(shash, tfm); int rc, i; - desc.shash.tfm = tfm; - desc.shash.flags = 0; + shash->tfm = tfm; + shash->flags = 0; hash->length = crypto_shash_digestsize(tfm); - rc = crypto_shash_init(&desc.shash); + rc = crypto_shash_init(shash); if (rc != 0) return rc; @@ -202,7 +508,7 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, u32 datalen = field_data[i].len; if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) { - rc = crypto_shash_update(&desc.shash, + rc = crypto_shash_update(shash, (const u8 *) &field_data[i].len, sizeof(field_data[i].len)); if (rc) @@ -212,13 +518,13 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, data_to_hash = buffer; datalen = IMA_EVENT_NAME_LEN_MAX + 1; } - rc = crypto_shash_update(&desc.shash, data_to_hash, datalen); + rc = crypto_shash_update(shash, data_to_hash, datalen); if (rc) break; } if (!rc) - rc = crypto_shash_final(&desc.shash, hash->digest); + rc = crypto_shash_final(shash, hash->digest); return rc; } @@ -259,15 +565,12 @@ static int __init ima_calc_boot_aggregate_tfm(char *digest, { u8 pcr_i[TPM_DIGEST_SIZE]; int rc, i; - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(tfm)]; - } desc; + SHASH_DESC_ON_STACK(shash, tfm); - desc.shash.tfm = tfm; - desc.shash.flags = 0; + shash->tfm = tfm; + shash->flags = 0; - rc = crypto_shash_init(&desc.shash); + rc = crypto_shash_init(shash); if (rc != 0) return rc; @@ -275,10 +578,10 @@ static int __init ima_calc_boot_aggregate_tfm(char *digest, for (i = TPM_PCR0; i < TPM_PCR8; i++) { ima_pcrread(i, pcr_i); /* now accumulate with current aggregate */ - rc = crypto_shash_update(&desc.shash, pcr_i, TPM_DIGEST_SIZE); + rc = crypto_shash_update(shash, pcr_i, TPM_DIGEST_SIZE); } if (!rc) - crypto_shash_final(&desc.shash, digest); + crypto_shash_final(shash, digest); return rc; } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index e8f9d70a465d..9164fc8cac84 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -43,7 +43,7 @@ int ima_used_chip; * a different value.) Violations add a zero entry to the measurement * list and extend the aggregate PCR value with ff...ff's. */ -static void __init ima_add_boot_aggregate(void) +static int __init ima_add_boot_aggregate(void) { static const char op[] = "add_boot_aggregate"; const char *audit_cause = "ENOMEM"; @@ -72,17 +72,23 @@ static void __init ima_add_boot_aggregate(void) result = ima_alloc_init_template(iint, NULL, boot_aggregate_name, NULL, 0, &entry); - if (result < 0) - return; + if (result < 0) { + audit_cause = "alloc_entry"; + goto err_out; + } result = ima_store_template(entry, violation, NULL, boot_aggregate_name); - if (result < 0) + if (result < 0) { ima_free_template_entry(entry); - return; + audit_cause = "store_entry"; + goto err_out; + } + return 0; err_out: integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op, audit_cause, result, 0); + return result; } int __init ima_init(void) @@ -98,6 +104,10 @@ 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); + if (rc) + return rc; + rc = ima_init_crypto(); if (rc) return rc; @@ -105,7 +115,10 @@ int __init ima_init(void) if (rc != 0) return rc; - ima_add_boot_aggregate(); /* boot aggregate must be first entry */ + rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */ + if (rc != 0) + return rc; + ima_init_policy(); return ima_fs_init(); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 09baa335ebc7..62f59eca32d3 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -77,46 +77,39 @@ __setup("ima_hash=", hash_setup); * could result in a file measurement error. * */ -static void ima_rdwr_violation_check(struct file *file) +static void ima_rdwr_violation_check(struct file *file, + struct integrity_iint_cache *iint, + int must_measure, + char **pathbuf, + const char **pathname) { struct inode *inode = file_inode(file); fmode_t mode = file->f_mode; bool send_tomtou = false, send_writers = false; - char *pathbuf = NULL; - const char *pathname; - - if (!S_ISREG(inode->i_mode) || !ima_initialized) - return; - - mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */ if (mode & FMODE_WRITE) { if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { - struct integrity_iint_cache *iint; - iint = integrity_iint_find(inode); + if (!iint) + iint = integrity_iint_find(inode); /* IMA_MEASURE is set from reader side */ if (iint && (iint->flags & IMA_MEASURE)) send_tomtou = true; } } else { - if ((atomic_read(&inode->i_writecount) > 0) && - ima_must_measure(inode, MAY_READ, FILE_CHECK)) + if ((atomic_read(&inode->i_writecount) > 0) && must_measure) send_writers = true; } - mutex_unlock(&inode->i_mutex); - if (!send_tomtou && !send_writers) return; - pathname = ima_d_path(&file->f_path, &pathbuf); + *pathname = ima_d_path(&file->f_path, pathbuf); if (send_tomtou) - ima_add_violation(file, pathname, "invalid_pcr", "ToMToU"); + ima_add_violation(file, *pathname, "invalid_pcr", "ToMToU"); if (send_writers) - ima_add_violation(file, pathname, + ima_add_violation(file, *pathname, "invalid_pcr", "open_writers"); - kfree(pathbuf); } static void ima_check_last_writer(struct integrity_iint_cache *iint, @@ -128,11 +121,13 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, return; mutex_lock(&inode->i_mutex); - if (atomic_read(&inode->i_writecount) == 1 && - iint->version != inode->i_version) { - iint->flags &= ~IMA_DONE_MASK; - if (iint->flags & IMA_APPRAISE) - ima_update_xattr(iint, file); + if (atomic_read(&inode->i_writecount) == 1) { + if ((iint->version != inode->i_version) || + (iint->flags & IMA_NEW_FILE)) { + iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); + if (iint->flags & IMA_APPRAISE) + ima_update_xattr(iint, file); + } } mutex_unlock(&inode->i_mutex); } @@ -158,19 +153,20 @@ void ima_file_free(struct file *file) ima_check_last_writer(iint, inode, file); } -static int process_measurement(struct file *file, const char *filename, - int mask, int function) +static int process_measurement(struct file *file, int mask, int function, + int opened) { struct inode *inode = file_inode(file); - struct integrity_iint_cache *iint; - struct ima_template_desc *template_desc = ima_template_desc_current(); + struct integrity_iint_cache *iint = NULL; + struct ima_template_desc *template_desc; char *pathbuf = NULL; const char *pathname = NULL; - int rc = -ENOMEM, action, must_appraise, _func; + int rc = -ENOMEM, action, must_appraise; struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; int xattr_len = 0; + bool violation_check; - if (!ima_initialized || !S_ISREG(inode->i_mode)) + if (!ima_policy_flag || !S_ISREG(inode->i_mode)) return 0; /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action @@ -178,19 +174,33 @@ static int process_measurement(struct file *file, const char *filename, * Included is the appraise submask. */ action = ima_get_action(inode, mask, function); - if (!action) + violation_check = ((function == FILE_CHECK || function == MMAP_CHECK) && + (ima_policy_flag & IMA_MEASURE)); + if (!action && !violation_check) return 0; must_appraise = action & IMA_APPRAISE; /* Is the appraise rule hook specific? */ - _func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function; + if (action & IMA_FILE_APPRAISE) + function = FILE_CHECK; mutex_lock(&inode->i_mutex); - iint = integrity_inode_get(inode); - if (!iint) - goto out; + if (action) { + iint = integrity_inode_get(inode); + if (!iint) + goto out; + } + + if (violation_check) { + ima_rdwr_violation_check(file, iint, action & IMA_MEASURE, + &pathbuf, &pathname); + if (!action) { + rc = 0; + goto out_free; + } + } /* Determine if already appraised/measured based on bitmask * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED, @@ -203,14 +213,13 @@ static int process_measurement(struct file *file, const char *filename, /* Nothing to do, just return existing appraised status */ if (!action) { if (must_appraise) - rc = ima_get_cache_status(iint, _func); + rc = ima_get_cache_status(iint, function); goto out_digsig; } - if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { - if (action & IMA_APPRAISE_SUBMASK) - xattr_ptr = &xattr_value; - } else + template_desc = ima_template_desc_current(); + if ((action & IMA_APPRAISE_SUBMASK) || + strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) xattr_ptr = &xattr_value; rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); @@ -220,23 +229,26 @@ static int process_measurement(struct file *file, const char *filename, goto out_digsig; } - pathname = filename ?: ima_d_path(&file->f_path, &pathbuf); + if (!pathname) /* ima_rdwr_violation possibly pre-fetched */ + pathname = ima_d_path(&file->f_path, &pathbuf); if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname, xattr_value, xattr_len); if (action & IMA_APPRAISE_SUBMASK) - rc = ima_appraise_measurement(_func, iint, file, pathname, - xattr_value, xattr_len); + rc = ima_appraise_measurement(function, iint, file, pathname, + xattr_value, xattr_len, opened); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); - kfree(pathbuf); + out_digsig: if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) rc = -EACCES; + kfree(xattr_value); +out_free: + kfree(pathbuf); out: mutex_unlock(&inode->i_mutex); - kfree(xattr_value); if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) return -EACCES; return 0; @@ -256,7 +268,7 @@ out: int ima_file_mmap(struct file *file, unsigned long prot) { if (file && (prot & PROT_EXEC)) - return process_measurement(file, NULL, MAY_EXEC, MMAP_CHECK); + return process_measurement(file, MAY_EXEC, MMAP_CHECK, 0); return 0; } @@ -275,10 +287,7 @@ int ima_file_mmap(struct file *file, unsigned long prot) */ int ima_bprm_check(struct linux_binprm *bprm) { - return process_measurement(bprm->file, - (strcmp(bprm->filename, bprm->interp) == 0) ? - bprm->filename : bprm->interp, - MAY_EXEC, BPRM_CHECK); + return process_measurement(bprm->file, MAY_EXEC, BPRM_CHECK, 0); } /** @@ -291,12 +300,11 @@ int ima_bprm_check(struct linux_binprm *bprm) * 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_file_check(struct file *file, int mask) +int ima_file_check(struct file *file, int mask, int opened) { - ima_rdwr_violation_check(file); - return process_measurement(file, NULL, + return process_measurement(file, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), - FILE_CHECK); + FILE_CHECK, opened); } EXPORT_SYMBOL_GPL(ima_file_check); @@ -319,7 +327,18 @@ int ima_module_check(struct file *file) #endif return 0; /* We rely on module signature checking */ } - return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK); + return process_measurement(file, MAY_EXEC, MODULE_CHECK, 0); +} + +int ima_fw_from_file(struct file *file, char *buf, size_t size) +{ + if (!file) { + if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && + (ima_appraise & IMA_APPRAISE_ENFORCE)) + return -EACCES; /* INTEGRITY_UNKNOWN */ + return 0; + } + return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, 0); } static int __init init_ima(void) @@ -328,8 +347,10 @@ static int __init init_ima(void) hash_setup(CONFIG_IMA_DEFAULT_HASH); error = ima_init(); - if (!error) + if (!error) { ima_initialized = 1; + ima_update_policy_flag(); + } return error; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 40a7488f6721..cdc620b2152f 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -35,6 +35,8 @@ #define DONT_APPRAISE 0x0008 #define AUDIT 0x0040 +int ima_policy_flag; + #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE @@ -84,6 +86,7 @@ static struct ima_rule_entry default_rules[] = { {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_MASK | IMA_UID}, {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, + {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, }; static struct ima_rule_entry default_appraise_rules[] = { @@ -241,6 +244,8 @@ static int get_subaction(struct ima_rule_entry *rule, int func) return IMA_BPRM_APPRAISE; case MODULE_CHECK: return IMA_MODULE_APPRAISE; + case FIRMWARE_CHECK: + return IMA_FIRMWARE_APPRAISE; case FILE_CHECK: default: return IMA_FILE_APPRAISE; @@ -292,6 +297,26 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, return action; } +/* + * Initialize the ima_policy_flag variable based on the currently + * loaded policy. Based on this flag, the decision to short circuit + * out of a function or not call the function in the first place + * can be made earlier. + */ +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; + } + + if (!ima_appraise) + ima_policy_flag &= ~IMA_APPRAISE; +} + /** * ima_init_policy - initialize the default measure rules. * @@ -332,12 +357,13 @@ void __init ima_init_policy(void) void ima_update_policy(void) { static const char op[] = "policy_update"; - const char *cause = "already exists"; + const char *cause = "already-exists"; int result = 1; int audit_info = 0; if (ima_rules == &ima_default_rules) { ima_rules = &ima_policy_rules; + ima_update_policy_flag(); cause = "complete"; result = 0; } @@ -486,6 +512,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = FILE_CHECK; else if (strcmp(args[0].from, "MODULE_CHECK") == 0) entry->func = MODULE_CHECK; + else if (strcmp(args[0].from, "FIRMWARE_CHECK") == 0) + entry->func = FIRMWARE_CHECK; else if ((strcmp(args[0].from, "FILE_MMAP") == 0) || (strcmp(args[0].from, "MMAP_CHECK") == 0)) entry->func = MMAP_CHECK; @@ -636,6 +664,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) result = -EINVAL; else if (entry->func == MODULE_CHECK) ima_appraise |= IMA_APPRAISE_MODULES; + else if (entry->func == FIRMWARE_CHECK) + ima_appraise |= IMA_APPRAISE_FIRMWARE; audit_log_format(ab, "res=%d", !result); audit_log_end(ab); return result; @@ -659,7 +689,7 @@ ssize_t ima_parse_add_rule(char *rule) /* Prevent installed policy from changing */ if (ima_rules != &ima_default_rules) { integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, - NULL, op, "already exists", + NULL, op, "already-exists", -EACCES, audit_info); return -EACCES; } @@ -685,7 +715,7 @@ ssize_t ima_parse_add_rule(char *rule) if (result) { kfree(entry); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, - NULL, op, "invalid policy", result, + NULL, op, "invalid-policy", result, audit_info); return result; } diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index a076a967ec47..e854862c9337 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -152,24 +152,6 @@ out: return result; } -static int init_defined_templates(void) -{ - int i = 0; - int result = 0; - - /* Init defined templates. */ - for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { - struct ima_template_desc *template = &defined_templates[i]; - - result = template_desc_init_fields(template->fmt, - &(template->fields), - &(template->num_fields)); - if (result < 0) - return result; - } - return result; -} - struct ima_template_desc *ima_template_desc_current(void) { if (!ima_template) @@ -178,13 +160,11 @@ struct ima_template_desc *ima_template_desc_current(void) return ima_template; } -int ima_init_template(void) +int __init ima_init_template(void) { - int result; - - result = init_defined_templates(); - if (result < 0) - return result; + struct ima_template_desc *template = ima_template_desc_current(); - return 0; + return template_desc_init_fields(template->fmt, + &(template->fields), + &(template->num_fields)); } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 33c0a70f6b15..9d1c2ebfe12a 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -31,6 +31,7 @@ #define IMA_DIGSIG 0x01000000 #define IMA_DIGSIG_REQUIRED 0x02000000 #define IMA_PERMIT_DIRECTIO 0x04000000 +#define IMA_NEW_FILE 0x08000000 #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ IMA_APPRAISE_SUBMASK) @@ -46,16 +47,21 @@ #define IMA_BPRM_APPRAISED 0x00002000 #define IMA_MODULE_APPRAISE 0x00004000 #define IMA_MODULE_APPRAISED 0x00008000 +#define IMA_FIRMWARE_APPRAISE 0x00010000 +#define IMA_FIRMWARE_APPRAISED 0x00020000 #define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \ - IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE) + IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE | \ + IMA_FIRMWARE_APPRAISE) #define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \ - IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED) + IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED | \ + IMA_FIRMWARE_APPRAISED) enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, EVM_XATTR_HMAC, EVM_IMA_XATTR_DIGSIG, IMA_XATTR_DIGEST_NG, + IMA_XATTR_LAST }; struct evm_ima_xattr_data { @@ -104,6 +110,7 @@ struct integrity_iint_cache { enum integrity_status ima_mmap_status:4; enum integrity_status ima_bprm_status:4; enum integrity_status ima_module_status:4; + enum integrity_status ima_firmware_status:4; enum integrity_status evm_status:4; struct ima_digest_data *ima_hash; }; @@ -111,7 +118,6 @@ struct integrity_iint_cache { /* rbtree tree calls to lookup, insert, delete * integrity data associated with an inode. */ -struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); #define INTEGRITY_KEYRING_EVM 0 @@ -124,6 +130,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode); int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen); +int integrity_init_keyring(const unsigned int id); #else static inline int integrity_digsig_verify(const unsigned int id, @@ -133,6 +140,10 @@ static inline int integrity_digsig_verify(const unsigned int id, return -EOPNOTSUPP; } +static inline int integrity_init_keyring(const unsigned int id) +{ + return 0; +} #endif /* CONFIG_INTEGRITY_SIGNATURE */ #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 8137b27d641d..b6adb94f6d52 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -33,9 +33,9 @@ MODULE_LICENSE("GPL"); */ struct key_type key_type_big_key = { .name = "big_key", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = big_key_instantiate, - .match = user_match, + .preparse = big_key_preparse, + .free_preparse = big_key_free_preparse, + .instantiate = generic_key_instantiate, .revoke = big_key_revoke, .destroy = big_key_destroy, .describe = big_key_describe, @@ -43,11 +43,11 @@ struct key_type key_type_big_key = { }; /* - * Instantiate a big key + * Preparse a big key */ -int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +int big_key_preparse(struct key_preparsed_payload *prep) { - struct path *path = (struct path *)&key->payload.data2; + struct path *path = (struct path *)&prep->payload; struct file *file; ssize_t written; size_t datalen = prep->datalen; @@ -58,11 +58,9 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) goto error; /* Set an arbitrary quota */ - ret = key_payload_reserve(key, 16); - if (ret < 0) - goto error; + prep->quotalen = 16; - key->type_data.x[1] = datalen; + prep->type_data[1] = (void *)(unsigned long)datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { /* Create a shmem file to store the data in. This will permit the data @@ -73,7 +71,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) file = shmem_kernel_file_setup("", datalen, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); - goto err_quota; + goto error; } written = kernel_write(file, prep->data, prep->datalen, 0); @@ -93,24 +91,33 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err_quota; - } + if (!data) + return -ENOMEM; - key->payload.data = memcpy(data, prep->data, prep->datalen); + prep->payload[0] = memcpy(data, prep->data, prep->datalen); } return 0; err_fput: fput(file); -err_quota: - key_payload_reserve(key, 0); error: return ret; } /* + * Clear preparsement. + */ +void big_key_free_preparse(struct key_preparsed_payload *prep) +{ + if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { + struct path *path = (struct path *)&prep->payload; + path_put(path); + } else { + kfree(prep->payload[0]); + } +} + +/* * dispose of the links from a revoked keyring * - called with the key sem write-locked */ diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 5fe443d120af..db9675db1026 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -970,7 +970,6 @@ struct key_type key_type_encrypted = { .name = "encrypted", .instantiate = encrypted_instantiate, .update = encrypted_update, - .match = user_match, .destroy = encrypted_destroy, .describe = user_describe, .read = encrypted_read, diff --git a/security/keys/gc.c b/security/keys/gc.c index d3222b6d7d59..9609a7f0faea 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -92,15 +92,6 @@ static void key_gc_timer_func(unsigned long data) } /* - * wait_on_bit() sleep function for uninterruptible waiting - */ -static int key_gc_wait_bit(void *flags) -{ - schedule(); - return 0; -} - -/* * Reap keys of dead type. * * We use three flags to make sure we see three complete cycles of the garbage @@ -123,7 +114,7 @@ void key_gc_keytype(struct key_type *ktype) schedule_work(&key_gc_work); kdebug("sleep"); - wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit, + wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, TASK_UNINTERRUPTIBLE); key_gc_dead_keytype = NULL; diff --git a/security/keys/internal.h b/security/keys/internal.h index 5f20da01fd8d..b8960c4959a5 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -107,20 +107,16 @@ extern int iterate_over_keyring(const struct key *keyring, int (*func)(const struct key *key, void *data), void *data); -typedef int (*key_match_func_t)(const struct key *, const void *); - struct keyring_search_context { struct keyring_index_key index_key; const struct cred *cred; - key_match_func_t match; - const void *match_data; + struct key_match_data match_data; unsigned flags; -#define KEYRING_SEARCH_LOOKUP_TYPE 0x0001 /* [as type->def_lookup_type] */ -#define KEYRING_SEARCH_NO_STATE_CHECK 0x0002 /* Skip state checks */ -#define KEYRING_SEARCH_DO_STATE_CHECK 0x0004 /* Override NO_STATE_CHECK */ -#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0008 /* Don't update times */ -#define KEYRING_SEARCH_NO_CHECK_PERM 0x0010 /* Don't check permissions */ -#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */ +#define KEYRING_SEARCH_NO_STATE_CHECK 0x0001 /* Skip state checks */ +#define KEYRING_SEARCH_DO_STATE_CHECK 0x0002 /* Override NO_STATE_CHECK */ +#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */ +#define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */ +#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */ int (*iterator)(const void *object, void *iterator_data); @@ -131,6 +127,8 @@ struct keyring_search_context { struct timespec now; }; +extern bool key_default_cmp(const struct key *key, + const struct key_match_data *match_data); extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, struct keyring_search_context *ctx); @@ -152,7 +150,8 @@ extern struct key *request_key_and_link(struct key_type *type, struct key *dest_keyring, unsigned long flags); -extern int lookup_user_key_possessed(const struct key *key, const void *target); +extern bool lookup_user_key_possessed(const struct key *key, + const struct key_match_data *match_data); extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, key_perm_t perm); #define KEY_LOOKUP_CREATE 0x01 diff --git a/security/keys/key.c b/security/keys/key.c index 2048a110e7f1..e17ba6aefdc0 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -27,8 +27,8 @@ DEFINE_SPINLOCK(key_serial_lock); struct rb_root key_user_tree; /* tree of quota records indexed by UID */ DEFINE_SPINLOCK(key_user_lock); -unsigned int key_quota_root_maxkeys = 200; /* root's key count quota */ -unsigned int key_quota_root_maxbytes = 20000; /* root's key space quota */ +unsigned int key_quota_root_maxkeys = 1000000; /* root's key count quota */ +unsigned int key_quota_root_maxbytes = 25000000; /* root's key space quota */ unsigned int key_quota_maxkeys = 200; /* general key count quota */ unsigned int key_quota_maxbytes = 20000; /* general key space quota */ @@ -437,6 +437,11 @@ static int __key_instantiate_and_link(struct key *key, /* disable the authorisation key */ if (authkey) key_revoke(authkey); + + if (prep->expiry != TIME_T_MAX) { + key->expiry = prep->expiry; + key_schedule_gc(prep->expiry + key_gc_delay); + } } } @@ -479,6 +484,7 @@ int key_instantiate_and_link(struct key *key, prep.data = data; prep.datalen = datalen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) @@ -488,7 +494,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret < 0) - goto error_free_preparse; + goto error; } ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit); @@ -496,10 +502,9 @@ int key_instantiate_and_link(struct key *key, if (keyring) __key_link_end(keyring, &key->index_key, edit); -error_free_preparse: +error: if (key->type->preparse) key->type->free_preparse(&prep); -error: return ret; } @@ -794,7 +799,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } key_ref = ERR_PTR(-EINVAL); - if (!index_key.type->match || !index_key.type->instantiate || + if (!index_key.type->instantiate || (!index_key.description && !index_key.type->preparse)) goto error_put_type; @@ -811,11 +816,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, prep.datalen = plen; prep.quotalen = index_key.type->def_datalen; prep.trusted = flags & KEY_ALLOC_TRUSTED; + prep.expiry = TIME_T_MAX; if (index_key.type->preparse) { ret = index_key.type->preparse(&prep); if (ret < 0) { key_ref = ERR_PTR(ret); - goto error_put_type; + goto error_free_prep; } if (!index_key.description) index_key.description = prep.description; @@ -941,6 +947,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) prep.data = payload; prep.datalen = plen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) @@ -956,9 +963,9 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) up_write(&key->sem); +error: if (key->type->preparse) key->type->free_preparse(&prep); -error: return ret; } EXPORT_SYMBOL(key_update); @@ -1024,6 +1031,38 @@ void key_invalidate(struct key *key) EXPORT_SYMBOL(key_invalidate); /** + * generic_key_instantiate - Simple instantiation of a key from preparsed data + * @key: The key to be instantiated + * @prep: The preparsed data to load. + * + * Instantiate a key from preparsed data. We assume we can just copy the data + * in directly and clear the old pointers. + * + * This can be pointed to directly by the key type instantiate op pointer. + */ +int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + pr_devel("==>%s()\n", __func__); + + ret = key_payload_reserve(key, prep->quotalen); + if (ret == 0) { + key->type_data.p[0] = prep->type_data[0]; + key->type_data.p[1] = prep->type_data[1]; + rcu_assign_keypointer(key, prep->payload[0]); + key->payload.data2[1] = prep->payload[1]; + prep->type_data[0] = NULL; + prep->type_data[1] = NULL; + prep->payload[0] = NULL; + prep->payload[1] = NULL; + } + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(generic_key_instantiate); + +/** * register_key_type - Register a type of key. * @ktype: The new key type. * diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index cd5bd0cef25d..eff88a5f5d40 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -86,6 +86,10 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, if (!*description) { kfree(description); description = NULL; + } else if ((description[0] == '.') && + (strncmp(type, "keyring", 7) == 0)) { + ret = -EPERM; + goto error2; } } @@ -404,12 +408,25 @@ long keyctl_invalidate_key(key_serial_t id) key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); + + /* Root is permitted to invalidate certain special keys */ + if (capable(CAP_SYS_ADMIN)) { + key_ref = lookup_user_key(id, 0, 0); + if (IS_ERR(key_ref)) + goto error; + if (test_bit(KEY_FLAG_ROOT_CAN_INVAL, + &key_ref_to_ptr(key_ref)->flags)) + goto invalidate; + goto error_put; + } + goto error; } +invalidate: key_invalidate(key_ref_to_ptr(key_ref)); ret = 0; - +error_put: key_ref_put(key_ref); error: kleave(" = %ld", ret); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 9cf2575f0d97..8177010174f7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -73,6 +73,8 @@ static inline unsigned keyring_hash(const char *desc) * can be treated as ordinary keys in addition to having their own special * operations. */ +static int keyring_preparse(struct key_preparsed_payload *prep); +static void keyring_free_preparse(struct key_preparsed_payload *prep); static int keyring_instantiate(struct key *keyring, struct key_preparsed_payload *prep); static void keyring_revoke(struct key *keyring); @@ -84,8 +86,9 @@ static long keyring_read(const struct key *keyring, struct key_type key_type_keyring = { .name = "keyring", .def_datalen = 0, + .preparse = keyring_preparse, + .free_preparse = keyring_free_preparse, .instantiate = keyring_instantiate, - .match = user_match, .revoke = keyring_revoke, .destroy = keyring_destroy, .describe = keyring_describe, @@ -123,6 +126,21 @@ static void keyring_publish_name(struct key *keyring) } /* + * Preparse a keyring payload + */ +static int keyring_preparse(struct key_preparsed_payload *prep) +{ + return prep->datalen != 0 ? -EINVAL : 0; +} + +/* + * Free a preparse of a user defined key payload + */ +static void keyring_free_preparse(struct key_preparsed_payload *prep) +{ +} + +/* * Initialise a keyring. * * Returns 0 on success, -EINVAL if given any data. @@ -130,17 +148,10 @@ static void keyring_publish_name(struct key *keyring) static int keyring_instantiate(struct key *keyring, struct key_preparsed_payload *prep) { - int ret; - - ret = -EINVAL; - if (prep->datalen == 0) { - assoc_array_init(&keyring->keys); - /* make the keyring available by name if it has one */ - keyring_publish_name(keyring); - ret = 0; - } - - return ret; + assoc_array_init(&keyring->keys); + /* make the keyring available by name if it has one */ + keyring_publish_name(keyring); + return 0; } /* @@ -500,6 +511,15 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, EXPORT_SYMBOL(keyring_alloc); /* + * By default, we keys found by getting an exact match on their descriptions. + */ +bool key_default_cmp(const struct key *key, + const struct key_match_data *match_data) +{ + return strcmp(key->description, match_data->raw_data) == 0; +} + +/* * Iteration function to consider each key found. */ static int keyring_search_iterator(const void *object, void *iterator_data) @@ -533,7 +553,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) } /* keys that don't match */ - if (!ctx->match(key, ctx->match_data)) { + if (!ctx->match_data.cmp(key, &ctx->match_data)) { kleave(" = 0 [!match]"); return 0; } @@ -573,8 +593,7 @@ skipped: */ static int search_keyring(struct key *keyring, struct keyring_search_context *ctx) { - if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) == - KEYRING_SEARCH_LOOKUP_DIRECT) { + if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_DIRECT) { const void *object; object = assoc_array_find(&keyring->keys, @@ -615,7 +634,7 @@ static bool search_nested_keyrings(struct key *keyring, /* Check to see if this top-level keyring is what we are looking for * and whether it is valid or not. */ - if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE || + if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE || keyring_compare_object(keyring, &ctx->index_key)) { ctx->skipped_ret = 2; ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK; @@ -873,16 +892,25 @@ key_ref_t keyring_search(key_ref_t keyring, .index_key.type = type, .index_key.description = description, .cred = current_cred(), - .match = type->match, - .match_data = description, - .flags = (type->def_lookup_type | - KEYRING_SEARCH_DO_STATE_CHECK), + .match_data.cmp = key_default_cmp, + .match_data.raw_data = description, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_DO_STATE_CHECK, }; + key_ref_t key; + int ret; + + if (type->match_preparse) { + ret = type->match_preparse(&ctx.match_data); + if (ret < 0) + return ERR_PTR(ret); + } - if (!ctx.match) - return ERR_PTR(-ENOKEY); + key = keyring_search_aux(keyring, &ctx); - return keyring_search_aux(keyring, &ctx); + if (type->match_free) + type->match_free(&ctx.match_data); + return key; } EXPORT_SYMBOL(keyring_search); @@ -1002,7 +1030,7 @@ static int keyring_detect_cycle_iterator(const void *object, /* We might get a keyring with matching index-key that is nonetheless a * different keyring. */ - if (key != ctx->match_data) + if (key != ctx->match_data.raw_data) return 0; ctx->result = ERR_PTR(-EDEADLK); @@ -1019,14 +1047,14 @@ static int keyring_detect_cycle_iterator(const void *object, static int keyring_detect_cycle(struct key *A, struct key *B) { struct keyring_search_context ctx = { - .index_key = A->index_key, - .match_data = A, - .iterator = keyring_detect_cycle_iterator, - .flags = (KEYRING_SEARCH_LOOKUP_DIRECT | - KEYRING_SEARCH_NO_STATE_CHECK | - KEYRING_SEARCH_NO_UPDATE_TIME | - KEYRING_SEARCH_NO_CHECK_PERM | - KEYRING_SEARCH_DETECT_TOO_DEEP), + .index_key = A->index_key, + .match_data.raw_data = A, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .iterator = keyring_detect_cycle_iterator, + .flags = (KEYRING_SEARCH_NO_STATE_CHECK | + KEYRING_SEARCH_NO_UPDATE_TIME | + KEYRING_SEARCH_NO_CHECK_PERM | + KEYRING_SEARCH_DETECT_TOO_DEEP), }; rcu_read_lock(); diff --git a/security/keys/proc.c b/security/keys/proc.c index d3f6f2fd21db..972eeb336b81 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -194,10 +194,10 @@ static int proc_keys_show(struct seq_file *m, void *v) .index_key.type = key->type, .index_key.description = key->description, .cred = current_cred(), - .match = lookup_user_key_possessed, - .match_data = key, - .flags = (KEYRING_SEARCH_NO_STATE_CHECK | - KEYRING_SEARCH_LOOKUP_DIRECT), + .match_data.cmp = lookup_user_key_possessed, + .match_data.raw_data = key, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_NO_STATE_CHECK, }; key_ref = make_key_ref(key, 0); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 0cf8a130a267..bd536cb221e2 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -489,9 +489,10 @@ found: /* * See if the key we're looking at is the target key. */ -int lookup_user_key_possessed(const struct key *key, const void *target) +bool lookup_user_key_possessed(const struct key *key, + const struct key_match_data *match_data) { - return key == target; + return key == match_data->raw_data; } /* @@ -516,9 +517,9 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, key_perm_t perm) { struct keyring_search_context ctx = { - .match = lookup_user_key_possessed, - .flags = (KEYRING_SEARCH_NO_STATE_CHECK | - KEYRING_SEARCH_LOOKUP_DIRECT), + .match_data.cmp = lookup_user_key_possessed, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .flags = KEYRING_SEARCH_NO_STATE_CHECK, }; struct request_key_auth *rka; struct key *key; @@ -673,7 +674,7 @@ try_again: ctx.index_key.type = key->type; ctx.index_key.description = key->description; ctx.index_key.desc_len = strlen(key->description); - ctx.match_data = key; + ctx.match_data.raw_data = key; kdebug("check possessed"); skey_ref = search_process_keyrings(&ctx); kdebug("possessed=%p", skey_ref); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 381411941cc1..bb4337c7ae1b 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -21,24 +21,6 @@ #define key_negative_timeout 60 /* default timeout on a negative key's existence */ -/* - * wait_on_bit() sleep function for uninterruptible waiting - */ -static int key_wait_bit(void *flags) -{ - schedule(); - return 0; -} - -/* - * wait_on_bit() sleep function for interruptible waiting - */ -static int key_wait_bit_intr(void *flags) -{ - schedule(); - return signal_pending(current) ? -ERESTARTSYS : 0; -} - /** * complete_request_key - Complete the construction of a key. * @cons: The key construction record. @@ -531,9 +513,9 @@ struct key *request_key_and_link(struct key_type *type, .index_key.type = type, .index_key.description = description, .cred = current_cred(), - .match = type->match, - .match_data = description, - .flags = KEYRING_SEARCH_LOOKUP_DIRECT, + .match_data.cmp = key_default_cmp, + .match_data.raw_data = description, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, }; struct key *key; key_ref_t key_ref; @@ -543,6 +525,14 @@ struct key *request_key_and_link(struct key_type *type, ctx.index_key.type->name, ctx.index_key.description, callout_info, callout_len, aux, dest_keyring, flags); + if (type->match_preparse) { + ret = type->match_preparse(&ctx.match_data); + if (ret < 0) { + key = ERR_PTR(ret); + goto error; + } + } + /* search all the process keyrings for a key */ key_ref = search_process_keyrings(&ctx); @@ -555,7 +545,7 @@ struct key *request_key_and_link(struct key_type *type, if (ret < 0) { key_put(key); key = ERR_PTR(ret); - goto error; + goto error_free; } } } else if (PTR_ERR(key_ref) != -EAGAIN) { @@ -565,12 +555,15 @@ struct key *request_key_and_link(struct key_type *type, * should consult userspace if we can */ key = ERR_PTR(-ENOKEY); if (!callout_info) - goto error; + goto error_free; key = construct_key_and_link(&ctx, callout_info, callout_len, aux, dest_keyring, flags); } +error_free: + if (type->match_free) + type->match_free(&ctx.match_data); error: kleave(" = %p", key); return key; @@ -592,10 +585,9 @@ int wait_for_key_construction(struct key *key, bool intr) int ret; ret = wait_on_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT, - intr ? key_wait_bit_intr : key_wait_bit, intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); - if (ret < 0) - return ret; + if (ret) + return -ERESTARTSYS; if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { smp_rmb(); return key->type_data.reject_error; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 7495a93b4b90..6639e2cb8853 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -20,6 +20,8 @@ #include "internal.h" #include <keys/user-type.h> +static int request_key_auth_preparse(struct key_preparsed_payload *); +static void request_key_auth_free_preparse(struct key_preparsed_payload *); static int request_key_auth_instantiate(struct key *, struct key_preparsed_payload *); static void request_key_auth_describe(const struct key *, struct seq_file *); @@ -33,6 +35,8 @@ static long request_key_auth_read(const struct key *, char __user *, size_t); struct key_type key_type_request_key_auth = { .name = ".request_key_auth", .def_datalen = sizeof(struct request_key_auth), + .preparse = request_key_auth_preparse, + .free_preparse = request_key_auth_free_preparse, .instantiate = request_key_auth_instantiate, .describe = request_key_auth_describe, .revoke = request_key_auth_revoke, @@ -40,6 +44,15 @@ struct key_type key_type_request_key_auth = { .read = request_key_auth_read, }; +static int request_key_auth_preparse(struct key_preparsed_payload *prep) +{ + return 0; +} + +static void request_key_auth_free_preparse(struct key_preparsed_payload *prep) +{ +} + /* * Instantiate a request-key authorisation key. */ @@ -233,9 +246,9 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) .index_key.type = &key_type_request_key_auth, .index_key.description = description, .cred = current_cred(), - .match = user_match, - .match_data = description, - .flags = KEYRING_SEARCH_LOOKUP_DIRECT, + .match_data.cmp = key_default_cmp, + .match_data.raw_data = description, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, }; struct key *authkey; key_ref_t authkey_ref; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 6b804aa4529a..c0594cb07ada 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1096,7 +1096,6 @@ struct key_type key_type_trusted = { .name = "trusted", .instantiate = trusted_instantiate, .update = trusted_update, - .match = user_match, .destroy = trusted_destroy, .describe = user_describe, .read = trusted_read, diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index faa2caeb593f..36b47bbd3d8c 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -26,10 +26,10 @@ static int logon_vet_description(const char *desc); */ struct key_type key_type_user = { .name = "user", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, @@ -46,10 +46,10 @@ EXPORT_SYMBOL_GPL(key_type_user); */ struct key_type key_type_logon = { .name = "logon", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, @@ -58,38 +58,37 @@ struct key_type key_type_logon = { EXPORT_SYMBOL_GPL(key_type_logon); /* - * instantiate a user defined key + * Preparse a user defined key payload */ -int user_instantiate(struct key *key, struct key_preparsed_payload *prep) +int user_preparse(struct key_preparsed_payload *prep) { struct user_key_payload *upayload; size_t datalen = prep->datalen; - int ret; - ret = -EINVAL; if (datalen <= 0 || datalen > 32767 || !prep->data) - goto error; - - ret = key_payload_reserve(key, datalen); - if (ret < 0) - goto error; + return -EINVAL; - ret = -ENOMEM; upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); if (!upayload) - goto error; + return -ENOMEM; /* attach the data */ + prep->quotalen = datalen; + prep->payload[0] = upayload; upayload->datalen = datalen; memcpy(upayload->data, prep->data, datalen); - rcu_assign_keypointer(key, upayload); - ret = 0; - -error: - return ret; + return 0; } +EXPORT_SYMBOL_GPL(user_preparse); -EXPORT_SYMBOL_GPL(user_instantiate); +/* + * Free a preparse of a user defined key payload + */ +void user_free_preparse(struct key_preparsed_payload *prep) +{ + kfree(prep->payload[0]); +} +EXPORT_SYMBOL_GPL(user_free_preparse); /* * update a user defined key @@ -136,16 +135,6 @@ error: EXPORT_SYMBOL_GPL(user_update); /* - * match users on their name - */ -int user_match(const struct key *key, const void *description) -{ - return strcmp(key->description, description) == 0; -} - -EXPORT_SYMBOL_GPL(user_match); - -/* * dispose of the links from a revoked keyring * - called with the key sem write-locked */ diff --git a/security/security.c b/security/security.c index 31614e9e96e5..18b35c63fc0c 100644 --- a/security/security.c +++ b/security/security.c @@ -775,9 +775,9 @@ int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) return security_ops->file_fcntl(file, cmd, arg); } -int security_file_set_fowner(struct file *file) +void security_file_set_fowner(struct file *file) { - return security_ops->file_set_fowner(file); + security_ops->file_set_fowner(file); } int security_file_send_sigiotask(struct task_struct *tsk, @@ -845,6 +845,17 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) return security_ops->kernel_create_files_as(new, inode); } +int security_kernel_fw_from_file(struct file *file, char *buf, size_t size) +{ + int ret; + + ret = security_ops->kernel_fw_from_file(file, buf, size); + if (ret) + return ret; + return ima_fw_from_file(file, buf, size); +} +EXPORT_SYMBOL_GPL(security_kernel_fw_from_file); + int security_kernel_module_request(char *kmod_name) { return security_ops->kernel_module_request(kmod_name); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 654f0710620a..c603b20356ad 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3381,14 +3381,12 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, return err; } -static int selinux_file_set_fowner(struct file *file) +static void selinux_file_set_fowner(struct file *file) { struct file_security_struct *fsec; fsec = file->f_security; fsec->fown_sid = current_sid(); - - return 0; } static int selinux_file_send_sigiotask(struct task_struct *tsk, diff --git a/security/selinux/netif.c b/security/selinux/netif.c index 485524c477a4..e607b4473ef6 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -275,7 +275,7 @@ static struct notifier_block sel_netif_netdev_notifier = { static __init int sel_netif_init(void) { - int i, err; + int i; if (!selinux_enabled) return 0; @@ -285,7 +285,7 @@ static __init int sel_netif_init(void) register_netdevice_notifier(&sel_netif_netdev_notifier); - return err; + return 0; } __initcall(sel_netif_init); diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index ddf315260839..da923f89d2a9 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -303,7 +303,6 @@ void sel_netnode_flush(void) static __init int sel_netnode_init(void) { int iter; - int ret; if (!selinux_enabled) return 0; @@ -313,7 +312,7 @@ static __init int sel_netnode_init(void) sel_netnode_hash[iter].size = 0; } - return ret; + return 0; } __initcall(sel_netnode_init); diff --git a/security/selinux/netport.c b/security/selinux/netport.c index 73ac6784d091..3311cc393cb4 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c @@ -237,7 +237,6 @@ void sel_netport_flush(void) static __init int sel_netport_init(void) { int iter; - int ret; if (!selinux_enabled) return 0; @@ -247,7 +246,7 @@ static __init int sel_netport_init(void) sel_netport_hash[iter].size = 0; } - return ret; + return 0; } __initcall(sel_netport_init); diff --git a/security/smack/Kconfig b/security/smack/Kconfig index e69de9c642b7..b065f9789418 100644 --- a/security/smack/Kconfig +++ b/security/smack/Kconfig @@ -12,3 +12,19 @@ config SECURITY_SMACK of other mandatory security schemes. If you are unsure how to answer this question, answer N. +config SECURITY_SMACK_BRINGUP + bool "Reporting on access granted by Smack rules" + depends on SECURITY_SMACK + default n + help + Enable the bring-up ("b") access mode in Smack rules. + When access is granted by a rule with the "b" mode a + message about the access requested is generated. The + intention is that a process can be granted a wide set + of access initially with the bringup mode set on the + rules. The developer can use the information to + identify which rules are necessary and what accesses + may be inappropriate. The developer can reduce the + access rule set once the behavior is well understood. + This is a superior mechanism to the oft abused + "permissive" mode of other systems. diff --git a/security/smack/smack.h b/security/smack/smack.h index 020307ef0972..b828a379377c 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -71,11 +71,11 @@ struct smack_known { #define SMK_CIPSOLEN 24 struct superblock_smack { - char *smk_root; - char *smk_floor; - char *smk_hat; - char *smk_default; - int smk_initialized; + struct smack_known *smk_root; + struct smack_known *smk_floor; + struct smack_known *smk_hat; + struct smack_known *smk_default; + int smk_initialized; }; struct socket_smack { @@ -88,7 +88,7 @@ struct socket_smack { * Inode smack data */ struct inode_smack { - char *smk_inode; /* label of the fso */ + struct smack_known *smk_inode; /* label of the fso */ struct smack_known *smk_task; /* label of the task */ struct smack_known *smk_mmap; /* label of the mmap domain */ struct mutex smk_lock; /* initialization lock */ @@ -112,7 +112,7 @@ struct task_smack { struct smack_rule { struct list_head list; struct smack_known *smk_subject; - char *smk_object; + struct smack_known *smk_object; int smk_access; }; @@ -123,7 +123,7 @@ struct smk_netlbladdr { struct list_head list; struct sockaddr_in smk_host; /* network address */ struct in_addr smk_mask; /* network mask */ - char *smk_label; /* label */ + struct smack_known *smk_label; /* label */ }; /* @@ -191,6 +191,7 @@ struct smk_port_label { */ #define MAY_TRANSMUTE 0x00001000 /* Controls directory labeling */ #define MAY_LOCK 0x00002000 /* Locks should be writes, but ... */ +#define MAY_BRINGUP 0x00004000 /* Report use of this rule */ /* * Just to make the common cases easier to deal with @@ -200,9 +201,9 @@ struct smk_port_label { #define MAY_NOT 0 /* - * Number of access types used by Smack (rwxatl) + * Number of access types used by Smack (rwxatlb) */ -#define SMK_NUM_ACCESS_TYPE 6 +#define SMK_NUM_ACCESS_TYPE 7 /* SMACK data */ struct smack_audit_data { @@ -226,23 +227,23 @@ struct smk_audit_info { /* * These functions are in smack_lsm.c */ -struct inode_smack *new_inode_smack(char *); +struct inode_smack *new_inode_smack(struct smack_known *); /* * These functions are in smack_access.c */ int smk_access_entry(char *, char *, struct list_head *); -int smk_access(struct smack_known *, char *, int, struct smk_audit_info *); -int smk_tskacc(struct task_smack *, char *, u32, struct smk_audit_info *); -int smk_curacc(char *, u32, struct smk_audit_info *); +int smk_access(struct smack_known *, struct smack_known *, + int, struct smk_audit_info *); +int smk_tskacc(struct task_smack *, struct smack_known *, + u32, struct smk_audit_info *); +int smk_curacc(struct smack_known *, u32, struct smk_audit_info *); struct smack_known *smack_from_secid(const u32); char *smk_parse_smack(const char *string, int len); int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); -char *smk_import(const char *, int); struct smack_known *smk_import_entry(const char *, int); void smk_insert_entry(struct smack_known *skp); struct smack_known *smk_find_entry(const char *); -u32 smack_to_secid(const char *); /* * Shared data. @@ -252,7 +253,7 @@ extern int smack_cipso_mapped; extern struct smack_known *smack_net_ambient; extern struct smack_known *smack_onlycap; extern struct smack_known *smack_syslog_label; -extern const char *smack_cipso_option; +extern struct smack_known smack_cipso_option; extern int smack_ptrace_rule; extern struct smack_known smack_known_floor; @@ -281,9 +282,9 @@ static inline int smk_inode_transmutable(const struct inode *isp) } /* - * Present a pointer to the smack label in an inode blob. + * Present a pointer to the smack label entry in an inode blob. */ -static inline char *smk_of_inode(const struct inode *isp) +static inline struct smack_known *smk_of_inode(const struct inode *isp) { struct inode_smack *sip = isp->i_security; return sip->smk_inode; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index f97d0842e621..5b970ffde024 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -94,7 +94,7 @@ int smk_access_entry(char *subject_label, char *object_label, struct smack_rule *srp; list_for_each_entry_rcu(srp, rule_list, list) { - if (srp->smk_object == object_label && + if (srp->smk_object->smk_known == object_label && srp->smk_subject->smk_known == subject_label) { may = srp->smk_access; break; @@ -111,8 +111,8 @@ int smk_access_entry(char *subject_label, char *object_label, /** * smk_access - determine if a subject has a specific access to an object - * @subject_known: a pointer to the subject's Smack label entry - * @object_label: a pointer to the object's Smack label + * @subject: a pointer to the subject's Smack label entry + * @object: a pointer to the object's Smack label entry * @request: the access requested, in "MAY" format * @a : a pointer to the audit data * @@ -122,8 +122,8 @@ int smk_access_entry(char *subject_label, char *object_label, * * Smack labels are shared on smack_list */ -int smk_access(struct smack_known *subject_known, char *object_label, - int request, struct smk_audit_info *a) +int smk_access(struct smack_known *subject, struct smack_known *object, + int request, struct smk_audit_info *a) { int may = MAY_NOT; int rc = 0; @@ -133,7 +133,7 @@ int smk_access(struct smack_known *subject_known, char *object_label, * * A star subject can't access any object. */ - if (subject_known == &smack_known_star) { + if (subject == &smack_known_star) { rc = -EACCES; goto out_audit; } @@ -142,28 +142,28 @@ int smk_access(struct smack_known *subject_known, char *object_label, * Tasks cannot be assigned the internet label. * An internet subject can access any object. */ - if (object_label == smack_known_web.smk_known || - subject_known == &smack_known_web) + if (object == &smack_known_web || + subject == &smack_known_web) goto out_audit; /* * A star object can be accessed by any subject. */ - if (object_label == smack_known_star.smk_known) + if (object == &smack_known_star) goto out_audit; /* * An object can be accessed in any way by a subject * with the same label. */ - if (subject_known->smk_known == object_label) + if (subject->smk_known == object->smk_known) goto out_audit; /* * A hat subject can read any object. * A floor object can be read by any subject. */ if ((request & MAY_ANYREAD) == request) { - if (object_label == smack_known_floor.smk_known) + if (object == &smack_known_floor) goto out_audit; - if (subject_known == &smack_known_hat) + if (subject == &smack_known_hat) goto out_audit; } /* @@ -174,27 +174,38 @@ int smk_access(struct smack_known *subject_known, char *object_label, * indicates there is no entry for this pair. */ rcu_read_lock(); - may = smk_access_entry(subject_known->smk_known, object_label, - &subject_known->smk_rules); + may = smk_access_entry(subject->smk_known, object->smk_known, + &subject->smk_rules); rcu_read_unlock(); - if (may > 0 && (request & may) == request) + if (may <= 0 || (request & may) != request) { + rc = -EACCES; goto out_audit; + } +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + /* + * Return a positive value if using bringup mode. + * This allows the hooks to identify checks that + * succeed because of "b" rules. + */ + if (may & MAY_BRINGUP) + rc = MAY_BRINGUP; +#endif - rc = -EACCES; out_audit: #ifdef CONFIG_AUDIT if (a) - smack_log(subject_known->smk_known, object_label, request, - rc, a); + smack_log(subject->smk_known, object->smk_known, + request, rc, a); #endif + return rc; } /** * smk_tskacc - determine if a task has a specific access to an object - * @tsp: a pointer to the subject task - * @obj_label: a pointer to the object's Smack label + * @tsp: a pointer to the subject's task + * @obj_known: a pointer to the object's label entry * @mode: the access requested, in "MAY" format * @a : common audit data * @@ -203,24 +214,25 @@ out_audit: * non zero otherwise. It allows that the task may have the capability * to override the rules. */ -int smk_tskacc(struct task_smack *subject, char *obj_label, +int smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known, u32 mode, struct smk_audit_info *a) { - struct smack_known *skp = smk_of_task(subject); + struct smack_known *sbj_known = smk_of_task(tsp); int may; int rc; /* * Check the global rule list */ - rc = smk_access(skp, obj_label, mode, NULL); - if (rc == 0) { + rc = smk_access(sbj_known, obj_known, mode, NULL); + if (rc >= 0) { /* * If there is an entry in the task's rule list * it can further restrict access. */ - may = smk_access_entry(skp->smk_known, obj_label, - &subject->smk_rules); + may = smk_access_entry(sbj_known->smk_known, + obj_known->smk_known, + &tsp->smk_rules); if (may < 0) goto out_audit; if ((mode & may) == mode) @@ -237,14 +249,15 @@ int smk_tskacc(struct task_smack *subject, char *obj_label, out_audit: #ifdef CONFIG_AUDIT if (a) - smack_log(skp->smk_known, obj_label, mode, rc, a); + smack_log(sbj_known->smk_known, obj_known->smk_known, + mode, rc, a); #endif return rc; } /** * smk_curacc - determine if current has a specific access to an object - * @obj_label: a pointer to the object's Smack label + * @obj_known: a pointer to the object's Smack label entry * @mode: the access requested, in "MAY" format * @a : common audit data * @@ -253,11 +266,12 @@ out_audit: * non zero otherwise. It allows that current may have the capability * to override the rules. */ -int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) +int smk_curacc(struct smack_known *obj_known, + u32 mode, struct smk_audit_info *a) { struct task_smack *tsp = current_security(); - return smk_tskacc(tsp, obj_label, mode, a); + return smk_tskacc(tsp, obj_known, mode, a); } #ifdef CONFIG_AUDIT @@ -328,6 +342,13 @@ void smack_log(char *subject_label, char *object_label, int request, struct smack_audit_data *sad; struct common_audit_data *a = &ad->a; +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + /* + * The result may be positive in bringup mode. + */ + if (result > 0) + result = 0; +#endif /* check if we have to log the current event */ if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0) return; @@ -544,27 +565,6 @@ unlockout: } /** - * smk_import - import a smack label - * @string: a text string that might be a Smack label - * @len: the maximum size, or zero if it is NULL terminated. - * - * Returns a pointer to the label in the label list that - * matches the passed string, adding it if necessary. - */ -char *smk_import(const char *string, int len) -{ - struct smack_known *skp; - - /* labels cannot begin with a '-' */ - if (string[0] == '-') - return NULL; - skp = smk_import_entry(string, len); - if (skp == NULL) - return NULL; - return skp->smk_known; -} - -/** * smack_from_secid - find the Smack label associated with a secid * @secid: an integer that might be associated with a Smack label * @@ -590,19 +590,3 @@ struct smack_known *smack_from_secid(const u32 secid) rcu_read_unlock(); return &smack_known_invalid; } - -/** - * smack_to_secid - find the secid associated with a Smack label - * @smack: the Smack label - * - * Returns the appropriate secid if there is one, - * otherwise 0 - */ -u32 smack_to_secid(const char *smack) -{ - struct smack_known *skp = smk_find_entry(smack); - - if (skp == NULL) - return 0; - return skp->smk_secid; -} diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index e6ab307ce86e..d515ec25ae9f 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -54,6 +54,151 @@ LIST_HEAD(smk_ipv6_port_list); +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static void smk_bu_mode(int mode, char *s) +{ + int i = 0; + + if (mode & MAY_READ) + s[i++] = 'r'; + if (mode & MAY_WRITE) + s[i++] = 'w'; + if (mode & MAY_EXEC) + s[i++] = 'x'; + if (mode & MAY_APPEND) + s[i++] = 'a'; + if (mode & MAY_TRANSMUTE) + s[i++] = 't'; + if (mode & MAY_LOCK) + s[i++] = 'l'; + if (i == 0) + s[i++] = '-'; + s[i] = '\0'; +} +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_note(char *note, struct smack_known *sskp, + struct smack_known *oskp, int mode, int rc) +{ + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) %s\n", + sskp->smk_known, oskp->smk_known, acc, note); + return 0; +} +#else +#define smk_bu_note(note, sskp, oskp, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_current(char *note, struct smack_known *oskp, + int mode, int rc) +{ + struct task_smack *tsp = current_security(); + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) %s %s\n", + tsp->smk_task->smk_known, oskp->smk_known, + acc, current->comm, note); + return 0; +} +#else +#define smk_bu_current(note, oskp, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_task(struct task_struct *otp, int mode, int rc) +{ + struct task_smack *tsp = current_security(); + struct task_smack *otsp = task_security(otp); + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) %s to %s\n", + tsp->smk_task->smk_known, otsp->smk_task->smk_known, acc, + current->comm, otp->comm); + return 0; +} +#else +#define smk_bu_task(otp, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_inode(struct inode *inode, int mode, int rc) +{ + struct task_smack *tsp = current_security(); + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) inode=(%s %ld) %s\n", + tsp->smk_task->smk_known, smk_of_inode(inode)->smk_known, acc, + inode->i_sb->s_id, inode->i_ino, current->comm); + return 0; +} +#else +#define smk_bu_inode(inode, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_file(struct file *file, int mode, int rc) +{ + struct task_smack *tsp = current_security(); + struct smack_known *sskp = tsp->smk_task; + struct inode *inode = file->f_inode; + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %s) %s\n", + sskp->smk_known, (char *)file->f_security, acc, + inode->i_sb->s_id, inode->i_ino, file->f_dentry->d_name.name, + current->comm); + return 0; +} +#else +#define smk_bu_file(file, mode, RC) (RC) +#endif + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +static int smk_bu_credfile(const struct cred *cred, struct file *file, + int mode, int rc) +{ + struct task_smack *tsp = cred->security; + struct smack_known *sskp = tsp->smk_task; + struct inode *inode = file->f_inode; + char acc[SMK_NUM_ACCESS_TYPE + 1]; + + if (rc <= 0) + return rc; + + smk_bu_mode(mode, acc); + pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %s) %s\n", + sskp->smk_known, smk_of_inode(inode)->smk_known, acc, + inode->i_sb->s_id, inode->i_ino, file->f_dentry->d_name.name, + current->comm); + return 0; +} +#else +#define smk_bu_credfile(cred, file, mode, RC) (RC) +#endif + /** * smk_fetch - Fetch the smack label from a file. * @ip: a pointer to the inode @@ -87,11 +232,11 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip, /** * new_inode_smack - allocate an inode security blob - * @smack: a pointer to the Smack label to use in the blob + * @skp: a pointer to the Smack label entry to use in the blob * * Returns the new blob or NULL if there's no memory available */ -struct inode_smack *new_inode_smack(char *smack) +struct inode_smack *new_inode_smack(struct smack_known *skp) { struct inode_smack *isp; @@ -99,7 +244,7 @@ struct inode_smack *new_inode_smack(char *smack) if (isp == NULL) return NULL; - isp->smk_inode = smack; + isp->smk_inode = skp; isp->smk_flags = 0; mutex_init(&isp->smk_lock); @@ -178,20 +323,20 @@ static inline unsigned int smk_ptrace_mode(unsigned int mode) /** * smk_ptrace_rule_check - helper for ptrace access * @tracer: tracer process - * @tracee_label: label of the process that's about to be traced, - * the pointer must originate from smack structures + * @tracee_known: label entry of the process that's about to be traced * @mode: ptrace attachment mode (PTRACE_MODE_*) * @func: name of the function that called us, used for audit * * Returns 0 on access granted, -error on error */ -static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label, +static int smk_ptrace_rule_check(struct task_struct *tracer, + struct smack_known *tracee_known, unsigned int mode, const char *func) { int rc; struct smk_audit_info ad, *saip = NULL; struct task_smack *tsp; - struct smack_known *skp; + struct smack_known *tracer_known; if ((mode & PTRACE_MODE_NOAUDIT) == 0) { smk_ad_init(&ad, func, LSM_AUDIT_DATA_TASK); @@ -200,12 +345,12 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label, } tsp = task_security(tracer); - skp = smk_of_task(tsp); + tracer_known = smk_of_task(tsp); if ((mode & PTRACE_MODE_ATTACH) && (smack_ptrace_rule == SMACK_PTRACE_EXACT || smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) { - if (skp->smk_known == tracee_label) + if (tracer_known->smk_known == tracee_known->smk_known) rc = 0; else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) rc = -EACCES; @@ -215,13 +360,15 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, char *tracee_label, rc = -EACCES; if (saip) - smack_log(skp->smk_known, tracee_label, 0, rc, saip); + smack_log(tracer_known->smk_known, + tracee_known->smk_known, + 0, rc, saip); return rc; } /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ - rc = smk_tskacc(tsp, tracee_label, smk_ptrace_mode(mode), saip); + rc = smk_tskacc(tsp, tracee_known, smk_ptrace_mode(mode), saip); return rc; } @@ -250,7 +397,7 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) skp = smk_of_task(task_security(ctp)); - rc = smk_ptrace_rule_check(current, skp->smk_known, mode, __func__); + rc = smk_ptrace_rule_check(current, skp, mode, __func__); return rc; } @@ -273,8 +420,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp) skp = smk_of_task(current_security()); - rc = smk_ptrace_rule_check(ptp, skp->smk_known, - PTRACE_MODE_ATTACH, __func__); + rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); return rc; } @@ -318,10 +464,10 @@ static int smack_sb_alloc_security(struct super_block *sb) if (sbsp == NULL) return -ENOMEM; - sbsp->smk_root = smack_known_floor.smk_known; - sbsp->smk_default = smack_known_floor.smk_known; - sbsp->smk_floor = smack_known_floor.smk_known; - sbsp->smk_hat = smack_known_hat.smk_known; + sbsp->smk_root = &smack_known_floor; + sbsp->smk_default = &smack_known_floor; + sbsp->smk_floor = &smack_known_floor; + sbsp->smk_hat = &smack_known_hat; /* * smk_initialized will be zero from kzalloc. */ @@ -405,7 +551,6 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) struct smack_known *skp; char *op; char *commap; - char *nsp; int transmute = 0; int specified = 0; @@ -421,38 +566,38 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) { op += strlen(SMK_FSHAT); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_hat = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_hat = skp; specified = 1; } } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) { op += strlen(SMK_FSFLOOR); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_floor = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_floor = skp; specified = 1; } } else if (strncmp(op, SMK_FSDEFAULT, strlen(SMK_FSDEFAULT)) == 0) { op += strlen(SMK_FSDEFAULT); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_default = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_default = skp; specified = 1; } } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) { op += strlen(SMK_FSROOT); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_root = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_root = skp; specified = 1; } } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) { op += strlen(SMK_FSTRANS); - nsp = smk_import(op, 0); - if (nsp != NULL) { - sp->smk_root = nsp; + skp = smk_import_entry(op, 0); + if (skp != NULL) { + sp->smk_root = skp; transmute = 1; specified = 1; } @@ -469,8 +614,8 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) * Unprivileged mounts get root and default from the caller. */ skp = smk_of_current(); - sp->smk_root = skp->smk_known; - sp->smk_default = skp->smk_known; + sp->smk_root = skp; + sp->smk_default = skp; } /* * Initialize the root inode. @@ -507,6 +652,7 @@ static int smack_sb_statfs(struct dentry *dentry) smk_ad_setfield_u_fs_path_dentry(&ad, dentry); rc = smk_curacc(sbp->smk_floor, MAY_READ, &ad); + rc = smk_bu_current("statfs", sbp->smk_floor, MAY_READ, rc); return rc; } @@ -546,7 +692,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) tracer = ptrace_parent(current); if (likely(tracer != NULL)) rc = smk_ptrace_rule_check(tracer, - isp->smk_task->smk_known, + isp->smk_task, PTRACE_MODE_ATTACH, __func__); rcu_read_unlock(); @@ -607,7 +753,7 @@ static int smack_inode_alloc_security(struct inode *inode) { struct smack_known *skp = smk_of_current(); - inode->i_security = new_inode_smack(skp->smk_known); + inode->i_security = new_inode_smack(skp); if (inode->i_security == NULL) return -ENOMEM; return 0; @@ -627,8 +773,8 @@ static void smack_inode_free_security(struct inode *inode) /** * smack_inode_init_security - copy out the smack from an inode - * @inode: the inode - * @dir: unused + * @inode: the newly created inode + * @dir: containing directory object * @qstr: unused * @name: where to put the attribute name * @value: where to put the attribute value @@ -642,8 +788,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, { struct inode_smack *issp = inode->i_security; struct smack_known *skp = smk_of_current(); - char *isp = smk_of_inode(inode); - char *dsp = smk_of_inode(dir); + struct smack_known *isp = smk_of_inode(inode); + struct smack_known *dsp = smk_of_inode(dir); int may; if (name) @@ -651,7 +797,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, if (value) { rcu_read_lock(); - may = smk_access_entry(skp->smk_known, dsp, &skp->smk_rules); + may = smk_access_entry(skp->smk_known, dsp->smk_known, + &skp->smk_rules); rcu_read_unlock(); /* @@ -666,13 +813,13 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, issp->smk_flags |= SMK_INODE_CHANGED; } - *value = kstrdup(isp, GFP_NOFS); + *value = kstrdup(isp->smk_known, GFP_NOFS); if (*value == NULL) return -ENOMEM; } if (len) - *len = strlen(isp) + 1; + *len = strlen(isp->smk_known); return 0; } @@ -688,7 +835,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { - char *isp; + struct smack_known *isp; struct smk_audit_info ad; int rc; @@ -697,11 +844,13 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, isp = smk_of_inode(old_dentry->d_inode); rc = smk_curacc(isp, MAY_WRITE, &ad); + rc = smk_bu_inode(old_dentry->d_inode, MAY_WRITE, rc); if (rc == 0 && new_dentry->d_inode != NULL) { isp = smk_of_inode(new_dentry->d_inode); smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry); rc = smk_curacc(isp, MAY_WRITE, &ad); + rc = smk_bu_inode(new_dentry->d_inode, MAY_WRITE, rc); } return rc; @@ -728,6 +877,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) * You need write access to the thing you're unlinking */ rc = smk_curacc(smk_of_inode(ip), MAY_WRITE, &ad); + rc = smk_bu_inode(ip, MAY_WRITE, rc); if (rc == 0) { /* * You also need write access to the containing directory @@ -735,6 +885,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, dir); rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); + rc = smk_bu_inode(dir, MAY_WRITE, rc); } return rc; } @@ -759,6 +910,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) * You need write access to the thing you're removing */ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_WRITE, rc); if (rc == 0) { /* * You also need write access to the containing directory @@ -766,6 +918,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, dir); rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); + rc = smk_bu_inode(dir, MAY_WRITE, rc); } return rc; @@ -773,10 +926,10 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) /** * smack_inode_rename - Smack check on rename - * @old_inode: the old directory - * @old_dentry: unused - * @new_inode: the new directory - * @new_dentry: unused + * @old_inode: unused + * @old_dentry: the old object + * @new_inode: unused + * @new_dentry: the new object * * Read and write access is required on both the old and * new directories. @@ -789,7 +942,7 @@ static int smack_inode_rename(struct inode *old_inode, struct dentry *new_dentry) { int rc; - char *isp; + struct smack_known *isp; struct smk_audit_info ad; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); @@ -797,11 +950,13 @@ static int smack_inode_rename(struct inode *old_inode, isp = smk_of_inode(old_dentry->d_inode); rc = smk_curacc(isp, MAY_READWRITE, &ad); + rc = smk_bu_inode(old_dentry->d_inode, MAY_READWRITE, rc); if (rc == 0 && new_dentry->d_inode != NULL) { isp = smk_of_inode(new_dentry->d_inode); smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry); rc = smk_curacc(isp, MAY_READWRITE, &ad); + rc = smk_bu_inode(new_dentry->d_inode, MAY_READWRITE, rc); } return rc; } @@ -819,6 +974,7 @@ static int smack_inode_permission(struct inode *inode, int mask) { struct smk_audit_info ad; int no_block = mask & MAY_NOT_BLOCK; + int rc; mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); /* @@ -832,7 +988,9 @@ static int smack_inode_permission(struct inode *inode, int mask) return -ECHILD; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, inode); - return smk_curacc(smk_of_inode(inode), mask, &ad); + rc = smk_curacc(smk_of_inode(inode), mask, &ad); + rc = smk_bu_inode(inode, mask, rc); + return rc; } /** @@ -845,6 +1003,8 @@ static int smack_inode_permission(struct inode *inode, int mask) static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) { struct smk_audit_info ad; + int rc; + /* * Need to allow for clearing the setuid bit. */ @@ -853,12 +1013,14 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_WRITE, rc); + return rc; } /** * smack_inode_getattr - Smack check for getting attributes - * @mnt: unused + * @mnt: vfsmount of the object * @dentry: the object * * Returns 0 if access is permitted, an error code otherwise @@ -867,21 +1029,24 @@ static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) { struct smk_audit_info ad; struct path path; + int rc; path.dentry = dentry; path.mnt = mnt; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, path); - return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_READ, rc); + return rc; } /** * smack_inode_setxattr - Smack check for setting xattrs * @dentry: the object * @name: name of the attribute - * @value: unused - * @size: unused + * @value: value of the attribute + * @size: size of the value * @flags: unused * * This protects the Smack attribute explicitly. @@ -923,7 +1088,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, rc = -EPERM; if (rc == 0 && check_import) { - skp = smk_import_entry(value, size); + skp = size ? smk_import_entry(value, size) : NULL; if (skp == NULL || (check_star && (skp == &smack_known_star || skp == &smack_known_web))) rc = -EINVAL; @@ -932,8 +1097,10 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - if (rc == 0) + if (rc == 0) { rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_WRITE, rc); + } return rc; } @@ -963,9 +1130,9 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, if (strcmp(name, XATTR_NAME_SMACK) == 0) { skp = smk_import_entry(value, size); if (skp != NULL) - isp->smk_inode = skp->smk_known; + isp->smk_inode = skp; else - isp->smk_inode = smack_known_invalid.smk_known; + isp->smk_inode = &smack_known_invalid; } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { skp = smk_import_entry(value, size); if (skp != NULL) @@ -993,11 +1160,14 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, static int smack_inode_getxattr(struct dentry *dentry, const char *name) { struct smk_audit_info ad; + int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_READ, rc); + return rc; } /** @@ -1033,6 +1203,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) smk_ad_setfield_u_fs_path_dentry(&ad, dentry); rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); + rc = smk_bu_inode(dentry->d_inode, MAY_WRITE, rc); if (rc != 0) return rc; @@ -1070,14 +1241,14 @@ static int smack_inode_getsecurity(const struct inode *inode, struct socket *sock; struct super_block *sbp; struct inode *ip = (struct inode *)inode; - char *isp; + struct smack_known *isp; int ilen; int rc = 0; if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { isp = smk_of_inode(inode); - ilen = strlen(isp) + 1; - *buffer = isp; + ilen = strlen(isp->smk_known); + *buffer = isp->smk_known; return ilen; } @@ -1095,15 +1266,15 @@ static int smack_inode_getsecurity(const struct inode *inode, ssp = sock->sk->sk_security; if (strcmp(name, XATTR_SMACK_IPIN) == 0) - isp = ssp->smk_in->smk_known; + isp = ssp->smk_in; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) - isp = ssp->smk_out->smk_known; + isp = ssp->smk_out; else return -EOPNOTSUPP; - ilen = strlen(isp) + 1; + ilen = strlen(isp->smk_known); if (rc == 0) { - *buffer = isp; + *buffer = isp->smk_known; rc = ilen; } @@ -1122,13 +1293,12 @@ static int smack_inode_getsecurity(const struct inode *inode, static int smack_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) { - int len = strlen(XATTR_NAME_SMACK); + int len = sizeof(XATTR_NAME_SMACK); - if (buffer != NULL && len <= buffer_size) { + if (buffer != NULL && len <= buffer_size) memcpy(buffer, XATTR_NAME_SMACK, len); - return len; - } - return -EINVAL; + + return len; } /** @@ -1140,7 +1310,7 @@ static void smack_inode_getsecid(const struct inode *inode, u32 *secid) { struct inode_smack *isp = inode->i_security; - *secid = smack_to_secid(isp->smk_inode); + *secid = isp->smk_inode->smk_secid; } /* @@ -1179,7 +1349,7 @@ static int smack_file_alloc_security(struct file *file) { struct smack_known *skp = smk_of_current(); - file->f_security = skp->smk_known; + file->f_security = skp; return 0; } @@ -1214,11 +1384,15 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - if (_IOC_DIR(cmd) & _IOC_WRITE) + if (_IOC_DIR(cmd) & _IOC_WRITE) { rc = smk_curacc(file->f_security, MAY_WRITE, &ad); + rc = smk_bu_file(file, MAY_WRITE, rc); + } - if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ)) + if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ)) { rc = smk_curacc(file->f_security, MAY_READ, &ad); + rc = smk_bu_file(file, MAY_READ, rc); + } return rc; } @@ -1233,10 +1407,13 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, static int smack_file_lock(struct file *file, unsigned int cmd) { struct smk_audit_info ad; + int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - return smk_curacc(file->f_security, MAY_LOCK, &ad); + rc = smk_curacc(file->f_security, MAY_LOCK, &ad); + rc = smk_bu_file(file, MAY_LOCK, rc); + return rc; } /** @@ -1266,12 +1443,14 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); rc = smk_curacc(file->f_security, MAY_LOCK, &ad); + rc = smk_bu_file(file, MAY_LOCK, rc); break; case F_SETOWN: case F_SETSIG: smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); rc = smk_curacc(file->f_security, MAY_WRITE, &ad); + rc = smk_bu_file(file, MAY_WRITE, rc); break; default: break; @@ -1298,7 +1477,7 @@ static int smack_mmap_file(struct file *file, struct smack_known *mkp; struct smack_rule *srp; struct task_smack *tsp; - char *osmack; + struct smack_known *okp; struct inode_smack *isp; int may; int mmay; @@ -1324,18 +1503,19 @@ static int smack_mmap_file(struct file *file, * to that rule's object label. */ list_for_each_entry_rcu(srp, &skp->smk_rules, list) { - osmack = srp->smk_object; + okp = srp->smk_object; /* * Matching labels always allows access. */ - if (mkp->smk_known == osmack) + if (mkp->smk_known == okp->smk_known) continue; /* * If there is a matching local rule take * that into account as well. */ - may = smk_access_entry(srp->smk_subject->smk_known, osmack, - &tsp->smk_rules); + may = smk_access_entry(srp->smk_subject->smk_known, + okp->smk_known, + &tsp->smk_rules); if (may == -ENOENT) may = srp->smk_access; else @@ -1352,8 +1532,8 @@ static int smack_mmap_file(struct file *file, * If there isn't one a SMACK64MMAP subject * can't have as much access as current. */ - mmay = smk_access_entry(mkp->smk_known, osmack, - &mkp->smk_rules); + mmay = smk_access_entry(mkp->smk_known, okp->smk_known, + &mkp->smk_rules); if (mmay == -ENOENT) { rc = -EACCES; break; @@ -1362,8 +1542,8 @@ static int smack_mmap_file(struct file *file, * If there is a local entry it modifies the * potential access, too. */ - tmay = smk_access_entry(mkp->smk_known, osmack, - &tsp->smk_rules); + tmay = smk_access_entry(mkp->smk_known, okp->smk_known, + &tsp->smk_rules); if (tmay != -ENOENT) mmay &= tmay; @@ -1390,12 +1570,11 @@ static int smack_mmap_file(struct file *file, * Returns 0 * Further research may be required on this one. */ -static int smack_file_set_fowner(struct file *file) +static void smack_file_set_fowner(struct file *file) { struct smack_known *skp = smk_of_current(); - file->f_security = skp->smk_known; - return 0; + file->f_security = skp; } /** @@ -1424,14 +1603,15 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, file = container_of(fown, struct file, f_owner); /* we don't log here as rc can be overriden */ - skp = smk_find_entry(file->f_security); - rc = smk_access(skp, tkp->smk_known, MAY_WRITE, NULL); + skp = file->f_security; + rc = smk_access(skp, tkp, MAY_WRITE, NULL); + rc = smk_bu_note("sigiotask", skp, tkp, MAY_WRITE, rc); if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) rc = 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, tsk); - smack_log(file->f_security, tkp->smk_known, MAY_WRITE, rc, &ad); + smack_log(skp->smk_known, tkp->smk_known, MAY_WRITE, rc, &ad); return rc; } @@ -1443,6 +1623,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, */ static int smack_file_receive(struct file *file) { + int rc; int may = 0; struct smk_audit_info ad; @@ -1456,7 +1637,9 @@ static int smack_file_receive(struct file *file) if (file->f_mode & FMODE_WRITE) may |= MAY_WRITE; - return smk_curacc(file->f_security, may, &ad); + rc = smk_curacc(file->f_security, may, &ad); + rc = smk_bu_file(file, may, rc); + return rc; } /** @@ -1478,12 +1661,15 @@ static int smack_file_open(struct file *file, const struct cred *cred) struct smk_audit_info ad; int rc; - if (smack_privileged(CAP_MAC_OVERRIDE)) + if (smack_privileged(CAP_MAC_OVERRIDE)) { + file->f_security = isp->smk_inode; return 0; + } smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); rc = smk_access(tsp->smk_task, isp->smk_inode, MAY_READ, &ad); + rc = smk_bu_credfile(cred, file, MAY_READ, rc); if (rc == 0) file->f_security = isp->smk_inode; @@ -1622,7 +1808,7 @@ static int smack_kernel_create_files_as(struct cred *new, struct inode_smack *isp = inode->i_security; struct task_smack *tsp = new->security; - tsp->smk_forked = smk_find_entry(isp->smk_inode); + tsp->smk_forked = isp->smk_inode; tsp->smk_task = tsp->smk_forked; return 0; } @@ -1640,10 +1826,13 @@ static int smk_curacc_on_task(struct task_struct *p, int access, { struct smk_audit_info ad; struct smack_known *skp = smk_of_task(task_security(p)); + int rc; smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); - return smk_curacc(skp->smk_known, access, &ad); + rc = smk_curacc(skp, access, &ad); + rc = smk_bu_task(p, access, rc); + return rc; } /** @@ -1797,6 +1986,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, struct smk_audit_info ad; struct smack_known *skp; struct smack_known *tkp = smk_of_task(task_security(p)); + int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); @@ -1804,15 +1994,20 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * Sending a signal requires that the sender * can write the receiver. */ - if (secid == 0) - return smk_curacc(tkp->smk_known, MAY_WRITE, &ad); + if (secid == 0) { + rc = smk_curacc(tkp, MAY_WRITE, &ad); + rc = smk_bu_task(p, MAY_WRITE, rc); + return rc; + } /* * If the secid isn't 0 we're dealing with some USB IO * specific behavior. This is not clean. For one thing * we can't take privilege into account. */ skp = smack_from_secid(secid); - return smk_access(skp, tkp->smk_known, MAY_WRITE, &ad); + rc = smk_access(skp, tkp, MAY_WRITE, &ad); + rc = smk_bu_note("USB signal", skp, tkp, MAY_WRITE, rc); + return rc; } /** @@ -1846,7 +2041,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) struct inode_smack *isp = inode->i_security; struct smack_known *skp = smk_of_task(task_security(p)); - isp->smk_inode = skp->smk_known; + isp->smk_inode = skp; } /* @@ -1904,7 +2099,7 @@ static void smack_sk_free_security(struct sock *sk) * * Returns the label of the far end or NULL if it's not special. */ -static char *smack_host_label(struct sockaddr_in *sip) +static struct smack_known *smack_host_label(struct sockaddr_in *sip) { struct smk_netlbladdr *snp; struct in_addr *siap = &sip->sin_addr; @@ -1921,7 +2116,7 @@ static char *smack_host_label(struct sockaddr_in *sip) if ((&snp->smk_host.sin_addr)->s_addr == (siap->s_addr & (&snp->smk_mask)->s_addr)) { /* we have found the special CIPSO option */ - if (snp->smk_label == smack_cipso_option) + if (snp->smk_label == &smack_cipso_option) return NULL; return snp->smk_label; } @@ -1986,13 +2181,13 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) struct smack_known *skp; int rc; int sk_lbl; - char *hostsp; + struct smack_known *hkp; struct socket_smack *ssp = sk->sk_security; struct smk_audit_info ad; rcu_read_lock(); - hostsp = smack_host_label(sap); - if (hostsp != NULL) { + hkp = smack_host_label(sap); + if (hkp != NULL) { #ifdef CONFIG_AUDIT struct lsm_network_audit net; @@ -2003,7 +2198,8 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) #endif sk_lbl = SMACK_UNLABELED_SOCKET; skp = ssp->smk_out; - rc = smk_access(skp, hostsp, MAY_WRITE, &ad); + rc = smk_access(skp, hkp, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 host check", skp, hkp, MAY_WRITE, rc); } else { sk_lbl = SMACK_CIPSO_SOCKET; rc = 0; @@ -2104,18 +2300,19 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, struct socket_smack *ssp = sk->sk_security; struct smack_known *skp; unsigned short port = 0; - char *object; + struct smack_known *object; struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT struct lsm_network_audit net; #endif if (act == SMK_RECEIVING) { skp = smack_net_ambient; - object = ssp->smk_in->smk_known; + object = ssp->smk_in; } else { skp = ssp->smk_out; - object = smack_net_ambient->smk_known; + object = smack_net_ambient; } /* @@ -2142,7 +2339,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, list_for_each_entry(spp, &smk_ipv6_port_list, list) { if (spp->smk_port != port) continue; - object = spp->smk_in->smk_known; + object = spp->smk_in; if (act == SMK_CONNECTING) ssp->smk_packet = spp->smk_out; break; @@ -2159,7 +2356,9 @@ auditout: else ad.a.u.net->v6info.daddr = address->sin6_addr; #endif - return smk_access(skp, object, MAY_WRITE, &ad); + rc = smk_access(skp, object, MAY_WRITE, &ad); + rc = smk_bu_note("IPv6 port check", skp, object, MAY_WRITE, rc); + return rc; } /** @@ -2191,7 +2390,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, return -EINVAL; if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { - nsp->smk_inode = skp->smk_known; + nsp->smk_inode = skp; nsp->smk_flags |= SMK_INODE_INSTANT; return 0; } @@ -2333,7 +2532,7 @@ static int smack_msg_msg_alloc_security(struct msg_msg *msg) { struct smack_known *skp = smk_of_current(); - msg->security = skp->smk_known; + msg->security = skp; return 0; } @@ -2354,9 +2553,9 @@ static void smack_msg_msg_free_security(struct msg_msg *msg) * * Returns a pointer to the smack value */ -static char *smack_of_shm(struct shmid_kernel *shp) +static struct smack_known *smack_of_shm(struct shmid_kernel *shp) { - return (char *)shp->shm_perm.security; + return (struct smack_known *)shp->shm_perm.security; } /** @@ -2370,7 +2569,7 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp) struct kern_ipc_perm *isp = &shp->shm_perm; struct smack_known *skp = smk_of_current(); - isp->security = skp->smk_known; + isp->security = skp; return 0; } @@ -2396,14 +2595,17 @@ static void smack_shm_free_security(struct shmid_kernel *shp) */ static int smk_curacc_shm(struct shmid_kernel *shp, int access) { - char *ssp = smack_of_shm(shp); + struct smack_known *ssp = smack_of_shm(shp); struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = shp->shm_perm.id; #endif - return smk_curacc(ssp, access, &ad); + rc = smk_curacc(ssp, access, &ad); + rc = smk_bu_current("shm", ssp, access, rc); + return rc; } /** @@ -2478,9 +2680,9 @@ static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, * * Returns a pointer to the smack value */ -static char *smack_of_sem(struct sem_array *sma) +static struct smack_known *smack_of_sem(struct sem_array *sma) { - return (char *)sma->sem_perm.security; + return (struct smack_known *)sma->sem_perm.security; } /** @@ -2494,7 +2696,7 @@ static int smack_sem_alloc_security(struct sem_array *sma) struct kern_ipc_perm *isp = &sma->sem_perm; struct smack_known *skp = smk_of_current(); - isp->security = skp->smk_known; + isp->security = skp; return 0; } @@ -2520,14 +2722,17 @@ static void smack_sem_free_security(struct sem_array *sma) */ static int smk_curacc_sem(struct sem_array *sma, int access) { - char *ssp = smack_of_sem(sma); + struct smack_known *ssp = smack_of_sem(sma); struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = sma->sem_perm.id; #endif - return smk_curacc(ssp, access, &ad); + rc = smk_curacc(ssp, access, &ad); + rc = smk_bu_current("sem", ssp, access, rc); + return rc; } /** @@ -2613,7 +2818,7 @@ static int smack_msg_queue_alloc_security(struct msg_queue *msq) struct kern_ipc_perm *kisp = &msq->q_perm; struct smack_known *skp = smk_of_current(); - kisp->security = skp->smk_known; + kisp->security = skp; return 0; } @@ -2634,11 +2839,11 @@ static void smack_msg_queue_free_security(struct msg_queue *msq) * smack_of_msq - the smack pointer for the msq * @msq: the object * - * Returns a pointer to the smack value + * Returns a pointer to the smack label entry */ -static char *smack_of_msq(struct msg_queue *msq) +static struct smack_known *smack_of_msq(struct msg_queue *msq) { - return (char *)msq->q_perm.security; + return (struct smack_known *)msq->q_perm.security; } /** @@ -2650,14 +2855,17 @@ static char *smack_of_msq(struct msg_queue *msq) */ static int smk_curacc_msq(struct msg_queue *msq, int access) { - char *msp = smack_of_msq(msq); + struct smack_known *msp = smack_of_msq(msq); struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = msq->q_perm.id; #endif - return smk_curacc(msp, access, &ad); + rc = smk_curacc(msp, access, &ad); + rc = smk_bu_current("msq", msp, access, rc); + return rc; } /** @@ -2750,15 +2958,18 @@ static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, */ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) { - char *isp = ipp->security; + struct smack_known *iskp = ipp->security; int may = smack_flags_to_may(flag); struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = ipp->id; #endif - return smk_curacc(isp, may, &ad); + rc = smk_curacc(iskp, may, &ad); + rc = smk_bu_current("svipc", iskp, may, rc); + return rc; } /** @@ -2768,9 +2979,9 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) */ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) { - char *smack = ipp->security; + struct smack_known *iskp = ipp->security; - *secid = smack_to_secid(smack); + *secid = iskp->smk_secid; } /** @@ -2787,7 +2998,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) struct inode_smack *isp; struct smack_known *skp; struct smack_known *ckp = smk_of_current(); - char *final; + struct smack_known *final; char trattr[TRANS_TRUE_SIZE]; int transflag = 0; int rc; @@ -2827,8 +3038,8 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * so there's no opportunity to set the mount * options. */ - sbsp->smk_root = smack_known_star.smk_known; - sbsp->smk_default = smack_known_star.smk_known; + sbsp->smk_root = &smack_known_star; + sbsp->smk_default = &smack_known_star; } isp->smk_inode = sbsp->smk_root; isp->smk_flags |= SMK_INODE_INSTANT; @@ -2858,7 +3069,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * * Cgroupfs is special */ - final = smack_known_star.smk_known; + final = &smack_known_star; break; case DEVPTS_SUPER_MAGIC: /* @@ -2866,7 +3077,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * Programs that change smack have to treat the * pty with respect. */ - final = ckp->smk_known; + final = ckp; break; case PROC_SUPER_MAGIC: /* @@ -2880,7 +3091,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * but watch out, because they're volitile, * getting recreated on every reboot. */ - final = smack_known_star.smk_known; + final = &smack_known_star; /* * No break. * @@ -2899,7 +3110,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * UNIX domain sockets use lower level socket data. */ if (S_ISSOCK(inode->i_mode)) { - final = smack_known_star.smk_known; + final = &smack_known_star; break; } /* @@ -2916,7 +3127,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) dp = dget(opt_dentry); skp = smk_fetch(XATTR_NAME_SMACK, inode, dp); if (skp != NULL) - final = skp->smk_known; + final = skp; /* * Transmuting directory @@ -2965,7 +3176,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) } if (final == NULL) - isp->smk_inode = ckp->smk_known; + isp->smk_inode = ckp; else isp->smk_inode = final; @@ -3090,9 +3301,13 @@ static int smack_unix_stream_connect(struct sock *sock, smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); smk_ad_setfield_u_net_sk(&ad, other); #endif - rc = smk_access(skp, okp->smk_known, MAY_WRITE, &ad); - if (rc == 0) - rc = smk_access(okp, okp->smk_known, MAY_WRITE, NULL); + rc = smk_access(skp, okp, MAY_WRITE, &ad); + rc = smk_bu_note("UDS connect", skp, okp, MAY_WRITE, rc); + if (rc == 0) { + rc = smk_access(okp, skp, MAY_WRITE, NULL); + rc = smk_bu_note("UDS connect", okp, skp, + MAY_WRITE, rc); + } } /* @@ -3118,8 +3333,8 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) { struct socket_smack *ssp = sock->sk->sk_security; struct socket_smack *osp = other->sk->sk_security; - struct smack_known *skp; struct smk_audit_info ad; + int rc; #ifdef CONFIG_AUDIT struct lsm_network_audit net; @@ -3131,8 +3346,9 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) if (smack_privileged(CAP_MAC_OVERRIDE)) return 0; - skp = ssp->smk_out; - return smk_access(skp, osp->smk_in->smk_known, MAY_WRITE, &ad); + rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); + rc = smk_bu_note("UDS send", ssp->smk_out, osp->smk_in, MAY_WRITE, rc); + return rc; } /** @@ -3346,7 +3562,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * This is the simplist possible security model * for networking. */ - rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in, + MAY_WRITE, rc); if (rc != 0) netlbl_skbuff_err(skb, rc, 0); break; @@ -3489,7 +3707,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct netlbl_lsm_secattr secattr; struct sockaddr_in addr; struct iphdr *hdr; - char *hsp; + struct smack_known *hskp; int rc; struct smk_audit_info ad; #ifdef CONFIG_AUDIT @@ -3526,7 +3744,8 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * Receiving a packet requires that the other end be able to write * here. Read access is not required. */ - rc = smk_access(skp, ssp->smk_in->smk_known, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_bu_note("IPv4 connect", skp, ssp->smk_in, MAY_WRITE, rc); if (rc != 0) return rc; @@ -3544,10 +3763,10 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, hdr = ip_hdr(skb); addr.sin_addr.s_addr = hdr->saddr; rcu_read_lock(); - hsp = smack_host_label(&addr); + hskp = smack_host_label(&addr); rcu_read_unlock(); - if (hsp == NULL) + if (hskp == NULL) rc = netlbl_req_setattr(req, &skp->smk_netlabel); else netlbl_req_delattr(req); @@ -3599,7 +3818,7 @@ static int smack_key_alloc(struct key *key, const struct cred *cred, { struct smack_known *skp = smk_of_task(cred->security); - key->security = skp->smk_known; + key->security = skp; return 0; } @@ -3630,6 +3849,7 @@ static int smack_key_permission(key_ref_t key_ref, struct smk_audit_info ad; struct smack_known *tkp = smk_of_task(cred->security); int request = 0; + int rc; keyp = key_ref_to_ptr(key_ref); if (keyp == NULL) @@ -3654,7 +3874,9 @@ static int smack_key_permission(key_ref_t key_ref, request = MAY_READ; if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR)) request = MAY_WRITE; - return smk_access(tkp, keyp->security, request, &ad); + rc = smk_access(tkp, keyp->security, request, &ad); + rc = smk_bu_note("key access", tkp, keyp->security, request, rc); + return rc; } #endif /* CONFIG_KEYS */ @@ -3685,6 +3907,7 @@ static int smack_key_permission(key_ref_t key_ref, */ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) { + struct smack_known *skp; char **rule = (char **)vrule; *rule = NULL; @@ -3694,7 +3917,9 @@ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) if (op != Audit_equal && op != Audit_not_equal) return -EINVAL; - *rule = smk_import(rulestr, 0); + skp = smk_import_entry(rulestr, 0); + if (skp) + *rule = skp->smk_known; return 0; } @@ -3813,7 +4038,12 @@ static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) */ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) { - *secid = smack_to_secid(secdata); + struct smack_known *skp = smk_find_entry(secdata); + + if (skp) + *secid = skp->smk_secid; + else + *secid = 0; return 0; } diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 3c720ff10591..bce4e8f1b267 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -131,14 +131,17 @@ LIST_HEAD(smack_rule_list); struct smack_parsed_rule { struct smack_known *smk_subject; - char *smk_object; + struct smack_known *smk_object; int smk_access1; int smk_access2; }; static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; -const char *smack_cipso_option = SMACK_CIPSO_OPTION; +struct smack_known smack_cipso_option = { + .smk_known = SMACK_CIPSO_OPTION, + .smk_secid = 0, +}; /* * Values for parsing cipso rules @@ -304,6 +307,10 @@ static int smk_perm_from_str(const char *string) case 'L': perm |= MAY_LOCK; break; + case 'b': + case 'B': + perm |= MAY_BRINGUP; + break; default: return perm; } @@ -335,7 +342,7 @@ static int smk_fill_rule(const char *subject, const char *object, if (rule->smk_subject == NULL) return -EINVAL; - rule->smk_object = smk_import(object, len); + rule->smk_object = smk_import_entry(object, len); if (rule->smk_object == NULL) return -EINVAL; } else { @@ -355,7 +362,7 @@ static int smk_fill_rule(const char *subject, const char *object, kfree(cp); if (skp == NULL) return -ENOENT; - rule->smk_object = skp->smk_known; + rule->smk_object = skp; } rule->smk_access1 = smk_perm_from_str(access1); @@ -594,13 +601,15 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) * anything you read back. */ if (strlen(srp->smk_subject->smk_known) >= max || - strlen(srp->smk_object) >= max) + strlen(srp->smk_object->smk_known) >= max) return; if (srp->smk_access == 0) return; - seq_printf(s, "%s %s", srp->smk_subject->smk_known, srp->smk_object); + seq_printf(s, "%s %s", + srp->smk_subject->smk_known, + srp->smk_object->smk_known); seq_putc(s, ' '); @@ -616,6 +625,8 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) seq_putc(s, 't'); if (srp->smk_access & MAY_LOCK) seq_putc(s, 'l'); + if (srp->smk_access & MAY_BRINGUP) + seq_putc(s, 'b'); seq_putc(s, '\n'); } @@ -1067,7 +1078,7 @@ static int netlbladdr_seq_show(struct seq_file *s, void *v) for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++); seq_printf(s, "%u.%u.%u.%u/%d %s\n", - hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label); + hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label->smk_known); return 0; } @@ -1147,10 +1158,10 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new) static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct smk_netlbladdr *skp; + struct smk_netlbladdr *snp; struct sockaddr_in newname; char *smack; - char *sp; + struct smack_known *skp; char *data; char *host = (char *)&newname.sin_addr.s_addr; int rc; @@ -1213,15 +1224,15 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, * If smack begins with '-', it is an option, don't import it */ if (smack[0] != '-') { - sp = smk_import(smack, 0); - if (sp == NULL) { + skp = smk_import_entry(smack, 0); + if (skp == NULL) { rc = -EINVAL; goto free_out; } } else { /* check known options */ - if (strcmp(smack, smack_cipso_option) == 0) - sp = (char *)smack_cipso_option; + if (strcmp(smack, smack_cipso_option.smk_known) == 0) + skp = &smack_cipso_option; else { rc = -EINVAL; goto free_out; @@ -1244,9 +1255,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, nsa = newname.sin_addr.s_addr; /* try to find if the prefix is already in the list */ found = 0; - list_for_each_entry_rcu(skp, &smk_netlbladdr_list, list) { - if (skp->smk_host.sin_addr.s_addr == nsa && - skp->smk_mask.s_addr == mask.s_addr) { + list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) { + if (snp->smk_host.sin_addr.s_addr == nsa && + snp->smk_mask.s_addr == mask.s_addr) { found = 1; break; } @@ -1254,26 +1265,26 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, smk_netlabel_audit_set(&audit_info); if (found == 0) { - skp = kzalloc(sizeof(*skp), GFP_KERNEL); - if (skp == NULL) + snp = kzalloc(sizeof(*snp), GFP_KERNEL); + if (snp == NULL) rc = -ENOMEM; else { rc = 0; - skp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr; - skp->smk_mask.s_addr = mask.s_addr; - skp->smk_label = sp; - smk_netlbladdr_insert(skp); + snp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr; + snp->smk_mask.s_addr = mask.s_addr; + snp->smk_label = skp; + smk_netlbladdr_insert(snp); } } else { /* we delete the unlabeled entry, only if the previous label * wasn't the special CIPSO option */ - if (skp->smk_label != smack_cipso_option) + if (snp->smk_label != &smack_cipso_option) rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, - &skp->smk_host.sin_addr, &skp->smk_mask, + &snp->smk_host.sin_addr, &snp->smk_mask, PF_INET, &audit_info); else rc = 0; - skp->smk_label = sp; + snp->smk_label = skp; } /* @@ -1281,10 +1292,10 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, * this host so that incoming packets get labeled. * but only if we didn't get the special CIPSO option */ - if (rc == 0 && sp != smack_cipso_option) + if (rc == 0 && skp != &smack_cipso_option) rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, - &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, - smack_to_secid(skp->smk_label), &audit_info); + &snp->smk_host.sin_addr, &snp->smk_mask, PF_INET, + snp->smk_label->smk_secid, &audit_info); if (rc == 0) rc = count; @@ -1677,7 +1688,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (smack_onlycap != NULL && smack_onlycap != skp) return -EPERM; - data = kzalloc(count, GFP_KERNEL); + data = kzalloc(count + 1, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -1880,7 +1891,10 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, else if (res != -ENOENT) return -EINVAL; - data[0] = res == 0 ? '1' : '0'; + /* + * smk_access() can return a value > 0 in the "bringup" case. + */ + data[0] = res >= 0 ? '1' : '0'; data[1] = '\0'; simple_transaction_set(file, 2); @@ -2228,7 +2242,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - data = kzalloc(count, GFP_KERNEL); + data = kzalloc(count + 1, GFP_KERNEL); if (data == NULL) return -ENOMEM; diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index c1b00375c9ad..3ffa4f5509d8 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -155,11 +155,9 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) u8 i; if (!buffer) return NULL; - { - struct timeval tv; - do_gettimeofday(&tv); - tomoyo_convert_time(tv.tv_sec, &stamp); - } + + tomoyo_convert_time(get_seconds(), &stamp); + pos = snprintf(buffer, tomoyo_buffer_len - 1, "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s " "granted=%s (global-pid=%u) task={ pid=%u ppid=%u " diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 283862aebdc8..e0fb75052550 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -2267,13 +2267,11 @@ static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; */ void tomoyo_update_stat(const u8 index) { - struct timeval tv; - do_gettimeofday(&tv); /* * I don't use atomic operations because race condition is not fatal. */ tomoyo_stat_updated[index]++; - tomoyo_stat_modified[index] = tv.tv_sec; + tomoyo_stat_modified[index] = get_seconds(); } /** diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index a3386d119425..bed745c8b1a3 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -173,7 +173,7 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, * Use filesystem name if filesystem does not support rename() * operation. */ - if (!inode->i_op->rename) + if (!inode->i_op->rename && !inode->i_op->rename2) goto prepend_filesystem_name; } /* Prepend device name. */ @@ -282,7 +282,8 @@ char *tomoyo_realpath_from_path(struct path *path) * Get local name for filesystems without rename() operation * or dentry without vfsmount. */ - if (!path->mnt || !inode->i_op->rename) + if (!path->mnt || + (!inode->i_op->rename && !inode->i_op->rename2)) pos = tomoyo_get_local_path(path->dentry, buf, buf_len - 1); /* Get absolute name for the rest. */ |