diff options
Diffstat (limited to 'security')
46 files changed, 1057 insertions, 367 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 729e595119ed..5923d5665209 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -381,7 +381,7 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, for (i = 0; i < AAFS_PROF_SIZEOF; i++) { new->dents[i] = old->dents[i]; if (new->dents[i]) - new->dents[i]->d_inode->i_mtime = CURRENT_TIME; + new->dents[i]->d_inode->i_mtime = current_time(new->dents[i]->d_inode); old->dents[i] = NULL; } } diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index fc3036b34e51..a4d90aa1045a 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -621,8 +621,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) /* released below */ cred = get_current_cred(); cxt = cred_cxt(cred); - profile = aa_cred_profile(cred); - previous_profile = cxt->previous; + profile = aa_get_newest_profile(aa_cred_profile(cred)); + previous_profile = aa_get_newest_profile(cxt->previous); if (unconfined(profile)) { info = "unconfined"; @@ -718,6 +718,8 @@ audit: out: aa_put_profile(hat); kfree(name); + aa_put_profile(profile); + aa_put_profile(previous_profile); put_cred(cred); return error; diff --git a/security/commoncap.c b/security/commoncap.c index 14540bd78561..8df676fbd393 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -310,13 +310,8 @@ int cap_inode_need_killpriv(struct dentry *dentry) struct inode *inode = d_backing_inode(dentry); int error; - if (!inode->i_op->getxattr) - return 0; - - error = inode->i_op->getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0); - if (error <= 0) - return 0; - return 1; + error = __vfs_getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0); + return error > 0; } /** @@ -329,12 +324,12 @@ int cap_inode_need_killpriv(struct dentry *dentry) */ int cap_inode_killpriv(struct dentry *dentry) { - struct inode *inode = d_backing_inode(dentry); - - if (!inode->i_op->removexattr) - return 0; + int error; - return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS); + error = __vfs_removexattr(dentry, XATTR_NAME_CAPS); + if (error == -EOPNOTSUPP) + error = 0; + return error; } /* @@ -394,11 +389,11 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data)); - if (!inode || !inode->i_op->getxattr) + if (!inode) return -ENODATA; - size = inode->i_op->getxattr((struct dentry *)dentry, inode, - XATTR_NAME_CAPS, &caps, XATTR_CAPS_SZ); + size = __vfs_getxattr((struct dentry *)dentry, inode, + XATTR_NAME_CAPS, &caps, XATTR_CAPS_SZ); if (size == -ENODATA || size == -EOPNOTSUPP) /* no data, that's ok */ return -ENODATA; diff --git a/security/inode.c b/security/inode.c index e3df905ab5b1..c83db05c15ab 100644 --- a/security/inode.c +++ b/security/inode.c @@ -117,7 +117,7 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode, inode->i_ino = get_next_ino(); inode->i_mode = mode; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); inode->i_private = data; if (is_dir) { inode->i_op = &simple_dir_inode_operations; @@ -156,12 +156,11 @@ EXPORT_SYMBOL_GPL(securityfs_create_file); * This function returns a pointer to a dentry if it succeeds. This * 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, %NULL will be returned. + * you are responsible here). If an error occurs, the function will return + * the error value (via ERR_PTR). * * If securityfs is not enabled in the kernel, the value %-ENODEV is - * returned. It is not wise to check for this value, but rather, check for - * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling - * code. + * returned. */ struct dentry *securityfs_create_dir(const char *name, struct dentry *parent) { diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 4304372b323f..106e855e2d9d 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -51,7 +51,7 @@ static bool init_keyring __initdata; int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen) { - if (id >= INTEGRITY_KEYRING_MAX) + if (id >= INTEGRITY_KEYRING_MAX || siglen < 2) return -EINVAL; if (!keyring[id]) { diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 11c1d30bd705..d7f282d75cc1 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -151,8 +151,16 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, memset(&hmac_misc, 0, sizeof(hmac_misc)); hmac_misc.ino = inode->i_ino; hmac_misc.generation = inode->i_generation; - hmac_misc.uid = from_kuid(inode->i_sb->s_user_ns, inode->i_uid); - hmac_misc.gid = from_kgid(inode->i_sb->s_user_ns, inode->i_gid); + /* The hmac uid and gid must be encoded in the initial user + * namespace (not the filesystems user namespace) as encoding + * them in the filesystems user namespace allows an attack + * where first they are written in an unprivileged fuse mount + * of a filesystem and then the system is tricked to mount the + * filesystem for real on next boot and trust it because + * everything is signed. + */ + hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid); + hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid); hmac_misc.mode = inode->i_mode; crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); if (evm_hmac_attrs & EVM_ATTR_FSUUID) @@ -182,8 +190,9 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, int error; int size; - if (!inode->i_op->getxattr) + if (!(inode->i_opflags & IOP_XATTR)) return -EOPNOTSUPP; + desc = init_desc(type); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -253,8 +262,8 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM, &xattr_data, sizeof(xattr_data), 0); - } else if (rc == -ENODATA && inode->i_op->removexattr) { - rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM); + } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) { + rc = __vfs_removexattr(dentry, XATTR_NAME_EVM); } return rc; } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index b9e26288d30c..e2ed498c0f5f 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -78,11 +78,11 @@ static int evm_find_protected_xattrs(struct dentry *dentry) int error; int count = 0; - if (!inode->i_op->getxattr) + if (!(inode->i_opflags & IOP_XATTR)) return -EOPNOTSUPP; for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) { - error = inode->i_op->getxattr(dentry, inode, *xattr, NULL, 0); + error = __vfs_getxattr(dentry, inode, *xattr, NULL, 0); if (error < 0) { if (error == -ENODATA) continue; @@ -145,6 +145,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, /* check value type */ switch (xattr_data->type) { case EVM_XATTR_HMAC: + if (xattr_len != sizeof(struct evm_ima_xattr_data)) { + evm_status = INTEGRITY_FAIL; + goto out; + } rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, calc.digest); if (rc) diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 5487827fa86c..370eb2f4dd37 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -27,6 +27,18 @@ config IMA to learn more about IMA. If unsure, say N. +config IMA_KEXEC + bool "Enable carrying the IMA measurement list across a soft boot" + depends on IMA && TCG_TPM && HAVE_IMA_KEXEC + default n + help + TPM PCRs are only reset on a hard reboot. In order to validate + a TPM's quote after a soft boot, the IMA measurement list of the + running kernel must be saved and restored on boot. + + Depending on the IMA policy, the measurement list can grow to + be very large. + config IMA_MEASURE_PCR_IDX int depends on IMA diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 9aeaedad1e2b..29f198bde02b 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o +ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index db25f54a04fe..5e6180a4da7d 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -28,6 +28,10 @@ #include "../integrity.h" +#ifdef CONFIG_HAVE_IMA_KEXEC +#include <asm/ima.h> +#endif + enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN, IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII }; enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; @@ -81,6 +85,7 @@ struct ima_template_field { /* IMA template descriptor definition */ struct ima_template_desc { + struct list_head list; char *name; char *fmt; int num_fields; @@ -102,6 +107,27 @@ struct ima_queue_entry { }; extern struct list_head ima_measurements; /* list of all measurements */ +/* Some details preceding the binary serialized measurement list */ +struct ima_kexec_hdr { + u16 version; + u16 _reserved0; + u32 _reserved1; + u64 buffer_size; + u64 count; +}; + +#ifdef CONFIG_HAVE_IMA_KEXEC +void ima_load_kexec_buffer(void); +#else +static inline void ima_load_kexec_buffer(void) {} +#endif /* CONFIG_HAVE_IMA_KEXEC */ + +/* + * The default binary_runtime_measurements list format is defined as the + * platform native format. The canonical format is defined as little-endian. + */ +extern bool ima_canonical_fmt; + /* Internal IMA function definitions */ int ima_init(void); int ima_fs_init(void); @@ -122,7 +148,12 @@ int ima_init_crypto(void); void ima_putc(struct seq_file *m, void *data, int datalen); void ima_print_digest(struct seq_file *m, u8 *digest, u32 size); struct ima_template_desc *ima_template_desc_current(void); +int ima_restore_measurement_entry(struct ima_template_entry *entry); +int ima_restore_measurement_list(loff_t bufsize, void *buf); +int ima_measurements_show(struct seq_file *m, void *v); +unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); +void ima_init_template_list(void); /* * used to protect h_table and sha_table diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 4b9b4a4e1b89..1fd9539a969d 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -130,6 +130,7 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len) { struct signature_v2_hdr *sig; + enum hash_algo ret; if (!xattr_value || xattr_len < 2) /* return default hash algo */ @@ -143,7 +144,9 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, return sig->hash_algo; break; case IMA_XATTR_DIGEST_NG: - return xattr_value->digest[0]; + ret = xattr_value->digest[0]; + if (ret < HASH_ALGO__LAST) + return ret; break; case IMA_XATTR_DIGEST: /* this is for backward compatibility */ @@ -165,13 +168,13 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int ima_read_xattr(struct dentry *dentry, struct evm_ima_xattr_data **xattr_value) { - struct inode *inode = d_backing_inode(dentry); - - if (!inode->i_op->getxattr) - return 0; + ssize_t ret; - return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value, - 0, GFP_NOFS); + ret = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value, + 0, GFP_NOFS); + if (ret == -EOPNOTSUPP) + ret = 0; + return ret; } /* @@ -190,12 +193,12 @@ int ima_appraise_measurement(enum ima_hooks func, { static const char op[] = "appraise_data"; char *cause = "unknown"; - struct dentry *dentry = file->f_path.dentry; + struct dentry *dentry = file_dentry(file); struct inode *inode = d_backing_inode(dentry); enum integrity_status status = INTEGRITY_UNKNOWN; int rc = xattr_len, hash_start = 0; - if (!inode->i_op->getxattr) + if (!(inode->i_opflags & IOP_XATTR)) return INTEGRITY_UNKNOWN; if (rc <= 0) { @@ -295,7 +298,7 @@ out: */ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) { - struct dentry *dentry = file->f_path.dentry; + struct dentry *dentry = file_dentry(file); int rc = 0; /* do not collect and update hash for digital signatures */ @@ -322,10 +325,10 @@ void ima_inode_post_setattr(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); struct integrity_iint_cache *iint; - int must_appraise, rc; + int must_appraise; if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode) - || !inode->i_op->removexattr) + || !(inode->i_opflags & IOP_XATTR)) return; must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); @@ -338,8 +341,7 @@ void ima_inode_post_setattr(struct dentry *dentry) iint->flags |= IMA_APPRAISE; } if (!must_appraise) - rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); - return; + __vfs_removexattr(dentry, XATTR_NAME_IMA); } /* @@ -385,14 +387,10 @@ 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) { - bool digsig; - if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST)) return -EINVAL; - digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG); - if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE)) - return -EPERM; - ima_reset_appraise_flags(d_backing_inode(dentry), digsig); + ima_reset_appraise_flags(d_backing_inode(dentry), + (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0); result = 0; } return result; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 38f2ed830dd6..802d5d20f36f 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -477,11 +477,13 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, u8 buffer[IMA_EVENT_NAME_LEN_MAX + 1] = { 0 }; u8 *data_to_hash = field_data[i].data; u32 datalen = field_data[i].len; + u32 datalen_to_hash = + !ima_canonical_fmt ? datalen : cpu_to_le32(datalen); if (strcmp(td->name, IMA_TEMPLATE_IMA_NAME) != 0) { rc = crypto_shash_update(shash, - (const u8 *) &field_data[i].len, - sizeof(field_data[i].len)); + (const u8 *) &datalen_to_hash, + sizeof(datalen_to_hash)); if (rc) break; } else if (strcmp(td->fields[i]->field_id, "n") == 0) { diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index c07a3844ea0a..ca303e5d2b94 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -28,6 +28,16 @@ static DEFINE_MUTEX(ima_write_mutex); +bool ima_canonical_fmt; +static int __init default_canonical_fmt_setup(char *str) +{ +#ifdef __BIG_ENDIAN + ima_canonical_fmt = 1; +#endif + return 1; +} +__setup("ima_canonical_fmt", default_canonical_fmt_setup); + static int valid_policy = 1; #define TMPBUFLEN 12 static ssize_t ima_show_htable_value(char __user *buf, size_t count, @@ -116,13 +126,13 @@ void ima_putc(struct seq_file *m, void *data, int datalen) * [eventdata length] * eventdata[n]=template specific data */ -static int ima_measurements_show(struct seq_file *m, void *v) +int ima_measurements_show(struct seq_file *m, void *v) { /* the list never shrinks, so we don't need a lock here */ struct ima_queue_entry *qe = v; struct ima_template_entry *e; char *template_name; - int namelen; + u32 pcr, namelen, template_data_len; /* temporary fields */ bool is_ima_template = false; int i; @@ -139,25 +149,29 @@ static int ima_measurements_show(struct seq_file *m, void *v) * PCR used defaults to the same (config option) in * little-endian format, unless set in policy */ - ima_putc(m, &e->pcr, sizeof(e->pcr)); + pcr = !ima_canonical_fmt ? e->pcr : cpu_to_le32(e->pcr); + ima_putc(m, &pcr, sizeof(e->pcr)); /* 2nd: template digest */ ima_putc(m, e->digest, TPM_DIGEST_SIZE); /* 3rd: template name size */ - namelen = strlen(template_name); + namelen = !ima_canonical_fmt ? strlen(template_name) : + cpu_to_le32(strlen(template_name)); ima_putc(m, &namelen, sizeof(namelen)); /* 4th: template name */ - ima_putc(m, template_name, namelen); + ima_putc(m, template_name, strlen(template_name)); /* 5th: template length (except for 'ima' template) */ if (strcmp(template_name, IMA_TEMPLATE_IMA_NAME) == 0) is_ima_template = true; - if (!is_ima_template) - ima_putc(m, &e->template_data_len, - sizeof(e->template_data_len)); + if (!is_ima_template) { + template_data_len = !ima_canonical_fmt ? e->template_data_len : + cpu_to_le32(e->template_data_len); + ima_putc(m, &template_data_len, sizeof(e->template_data_len)); + } /* 6th: template specific data */ for (i = 0; i < e->template_desc->num_fields; i++) { @@ -401,7 +415,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) const char *cause = valid_policy ? "completed" : "failed"; if ((file->f_flags & O_ACCMODE) == O_RDONLY) - return 0; + return seq_release(inode, file); if (valid_policy && ima_check_policy() < 0) { cause = "failed"; diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 32912bd54ead..2967d497a665 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -115,7 +115,8 @@ int __init ima_init(void) ima_used_chip = 1; if (!ima_used_chip) - pr_info("No TPM chip found, activating TPM-bypass!\n"); + pr_info("No TPM chip found, activating TPM-bypass! (rc=%d)\n", + rc); rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA); if (rc) @@ -128,6 +129,8 @@ int __init ima_init(void) if (rc != 0) return rc; + ima_load_kexec_buffer(); + rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */ if (rc != 0) return rc; diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c new file mode 100644 index 000000000000..e473eee913cb --- /dev/null +++ b/security/integrity/ima/ima_kexec.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2016 IBM Corporation + * + * Authors: + * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> + * Mimi Zohar <zohar@linux.vnet.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include <linux/seq_file.h> +#include <linux/vmalloc.h> +#include <linux/kexec.h> +#include "ima.h" + +#ifdef CONFIG_IMA_KEXEC +static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, + unsigned long segment_size) +{ + struct ima_queue_entry *qe; + struct seq_file file; + struct ima_kexec_hdr khdr; + int ret = 0; + + /* segment size can't change between kexec load and execute */ + file.buf = vmalloc(segment_size); + if (!file.buf) { + ret = -ENOMEM; + goto out; + } + + file.size = segment_size; + file.read_pos = 0; + file.count = sizeof(khdr); /* reserved space */ + + memset(&khdr, 0, sizeof(khdr)); + khdr.version = 1; + list_for_each_entry_rcu(qe, &ima_measurements, later) { + if (file.count < file.size) { + khdr.count++; + ima_measurements_show(&file, qe); + } else { + ret = -EINVAL; + break; + } + } + + if (ret < 0) + goto out; + + /* + * fill in reserved space with some buffer details + * (eg. version, buffer size, number of measurements) + */ + khdr.buffer_size = file.count; + if (ima_canonical_fmt) { + khdr.version = cpu_to_le16(khdr.version); + khdr.count = cpu_to_le64(khdr.count); + khdr.buffer_size = cpu_to_le64(khdr.buffer_size); + } + memcpy(file.buf, &khdr, sizeof(khdr)); + + print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE, + 16, 1, file.buf, + file.count < 100 ? file.count : 100, true); + + *buffer_size = file.count; + *buffer = file.buf; +out: + if (ret == -EINVAL) + vfree(file.buf); + return ret; +} + +/* + * Called during kexec_file_load so that IMA can add a segment to the kexec + * image for the measurement list for the next kernel. + * + * This function assumes that kexec_mutex is held. + */ +void ima_add_kexec_buffer(struct kimage *image) +{ + struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE, + .buf_min = 0, .buf_max = ULONG_MAX, + .top_down = true }; + unsigned long binary_runtime_size; + + /* use more understandable variable names than defined in kbuf */ + void *kexec_buffer = NULL; + size_t kexec_buffer_size; + size_t kexec_segment_size; + int ret; + + /* + * Reserve an extra half page of memory for additional measurements + * added during the kexec load. + */ + binary_runtime_size = ima_get_binary_runtime_size(); + if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) + kexec_segment_size = ULONG_MAX; + else + kexec_segment_size = ALIGN(ima_get_binary_runtime_size() + + PAGE_SIZE / 2, PAGE_SIZE); + if ((kexec_segment_size == ULONG_MAX) || + ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages / 2)) { + pr_err("Binary measurement list too large.\n"); + return; + } + + ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, + kexec_segment_size); + if (!kexec_buffer) { + pr_err("Not enough memory for the kexec measurement buffer.\n"); + return; + } + + kbuf.buffer = kexec_buffer; + kbuf.bufsz = kexec_buffer_size; + kbuf.memsz = kexec_segment_size; + ret = kexec_add_buffer(&kbuf); + if (ret) { + pr_err("Error passing over kexec measurement buffer.\n"); + return; + } + + ret = arch_ima_add_kexec_buffer(image, kbuf.mem, kexec_segment_size); + if (ret) { + pr_err("Error passing over kexec measurement buffer.\n"); + return; + } + + pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n", + kbuf.mem); +} +#endif /* IMA_KEXEC */ + +/* + * Restore the measurement list from the previous kernel. + */ +void ima_load_kexec_buffer(void) +{ + void *kexec_buffer = NULL; + size_t kexec_buffer_size = 0; + int rc; + + rc = ima_get_kexec_buffer(&kexec_buffer, &kexec_buffer_size); + switch (rc) { + case 0: + rc = ima_restore_measurement_list(kexec_buffer_size, + kexec_buffer); + if (rc != 0) + pr_err("Failed to restore the measurement list: %d\n", + rc); + + ima_free_kexec_buffer(); + break; + case -ENOTSUPP: + pr_debug("Restoring the measurement list not supported\n"); + break; + case -ENOENT: + pr_debug("No measurement list to restore\n"); + break; + default: + pr_debug("Error restoring the measurement list: %d\n", rc); + } +} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 596ef616ac21..50818c60538b 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -228,7 +228,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size, if ((action & IMA_APPRAISE_SUBMASK) || strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) /* read 'security.ima' */ - xattr_len = ima_read_xattr(file->f_path.dentry, &xattr_value); + xattr_len = ima_read_xattr(file_dentry(file), &xattr_value); hash_algo = ima_get_hash_algo(xattr_value, xattr_len); @@ -418,6 +418,7 @@ static int __init init_ima(void) { int error; + ima_init_template_list(); hash_setup(CONFIG_IMA_DEFAULT_HASH); error = ima_init(); if (!error) { diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 32f6ac0f96df..d9aa5ab71204 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -29,6 +29,11 @@ #define AUDIT_CAUSE_LEN_MAX 32 LIST_HEAD(ima_measurements); /* list of all measurements */ +#ifdef CONFIG_IMA_KEXEC +static unsigned long binary_runtime_size; +#else +static unsigned long binary_runtime_size = ULONG_MAX; +#endif /* key: inode (before secure-hashing a file) */ struct ima_h_table ima_htable = { @@ -64,12 +69,32 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, return ret; } +/* + * Calculate the memory required for serializing a single + * binary_runtime_measurement list entry, which contains a + * couple of variable length fields (e.g template name and data). + */ +static int get_binary_runtime_size(struct ima_template_entry *entry) +{ + int size = 0; + + size += sizeof(u32); /* pcr */ + size += sizeof(entry->digest); + size += sizeof(int); /* template name size field */ + size += strlen(entry->template_desc->name) + 1; + size += sizeof(entry->template_data_len); + size += entry->template_data_len; + return size; +} + /* ima_add_template_entry helper function: - * - Add template entry to measurement list and hash table. + * - Add template entry to the measurement list and hash table, for + * all entries except those carried across kexec. * * (Called with ima_extend_list_mutex held.) */ -static int ima_add_digest_entry(struct ima_template_entry *entry) +static int ima_add_digest_entry(struct ima_template_entry *entry, + bool update_htable) { struct ima_queue_entry *qe; unsigned int key; @@ -85,11 +110,34 @@ static int ima_add_digest_entry(struct ima_template_entry *entry) list_add_tail_rcu(&qe->later, &ima_measurements); atomic_long_inc(&ima_htable.len); - key = ima_hash_key(entry->digest); - hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]); + if (update_htable) { + key = ima_hash_key(entry->digest); + hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]); + } + + if (binary_runtime_size != ULONG_MAX) { + int size; + + size = get_binary_runtime_size(entry); + binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ? + binary_runtime_size + size : ULONG_MAX; + } return 0; } +/* + * Return the amount of memory required for serializing the + * entire binary_runtime_measurement list, including the ima_kexec_hdr + * structure. + */ +unsigned long ima_get_binary_runtime_size(void) +{ + if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr))) + return ULONG_MAX; + else + return binary_runtime_size + sizeof(struct ima_kexec_hdr); +}; + static int ima_pcr_extend(const u8 *hash, int pcr) { int result = 0; @@ -103,8 +151,13 @@ static int ima_pcr_extend(const u8 *hash, int pcr) return result; } -/* Add template entry to the measurement list and hash table, - * and extend the pcr. +/* + * Add template entry to the measurement list and hash table, and + * extend the pcr. + * + * On systems which support carrying the IMA measurement list across + * kexec, maintain the total memory size required for serializing the + * binary_runtime_measurements. */ int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, @@ -126,7 +179,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, } } - result = ima_add_digest_entry(entry); + result = ima_add_digest_entry(entry, 1); if (result < 0) { audit_cause = "ENOMEM"; audit_info = 0; @@ -149,3 +202,13 @@ out: op, audit_cause, result, audit_info); return result; } + +int ima_restore_measurement_entry(struct ima_template_entry *entry) +{ + int result = 0; + + mutex_lock(&ima_extend_list_mutex); + result = ima_add_digest_entry(entry, 0); + mutex_unlock(&ima_extend_list_mutex); + return result; +} diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index febd12ed9b55..cebb37c63629 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -15,16 +15,20 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/rculist.h> #include "ima.h" #include "ima_template_lib.h" -static struct ima_template_desc defined_templates[] = { +static struct ima_template_desc builtin_templates[] = { {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT}, {.name = "ima-ng", .fmt = "d-ng|n-ng"}, {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"}, {.name = "", .fmt = ""}, /* placeholder for a custom format */ }; +static LIST_HEAD(defined_templates); +static DEFINE_SPINLOCK(template_list); + static struct ima_template_field supported_fields[] = { {.field_id = "d", .field_init = ima_eventdigest_init, .field_show = ima_show_template_digest}, @@ -37,6 +41,7 @@ static struct ima_template_field supported_fields[] = { {.field_id = "sig", .field_init = ima_eventsig_init, .field_show = ima_show_template_sig}, }; +#define MAX_TEMPLATE_NAME_LEN 15 static struct ima_template_desc *ima_template; static struct ima_template_desc *lookup_template_desc(const char *name); @@ -52,6 +57,8 @@ static int __init ima_template_setup(char *str) if (ima_template) return 1; + ima_init_template_list(); + /* * Verify that a template with the supplied name exists. * If not, use CONFIG_IMA_DEFAULT_TEMPLATE. @@ -80,7 +87,7 @@ __setup("ima_template=", ima_template_setup); static int __init ima_template_fmt_setup(char *str) { - int num_templates = ARRAY_SIZE(defined_templates); + int num_templates = ARRAY_SIZE(builtin_templates); if (ima_template) return 1; @@ -91,22 +98,28 @@ static int __init ima_template_fmt_setup(char *str) return 1; } - defined_templates[num_templates - 1].fmt = str; - ima_template = defined_templates + num_templates - 1; + builtin_templates[num_templates - 1].fmt = str; + ima_template = builtin_templates + num_templates - 1; + return 1; } __setup("ima_template_fmt=", ima_template_fmt_setup); static struct ima_template_desc *lookup_template_desc(const char *name) { - int i; - - for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { - if (strcmp(defined_templates[i].name, name) == 0) - return defined_templates + i; + struct ima_template_desc *template_desc; + int found = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(template_desc, &defined_templates, list) { + if ((strcmp(template_desc->name, name) == 0) || + (strcmp(template_desc->fmt, name) == 0)) { + found = 1; + break; + } } - - return NULL; + rcu_read_unlock(); + return found ? template_desc : NULL; } static struct ima_template_field *lookup_template_field(const char *field_id) @@ -142,9 +155,14 @@ static int template_desc_init_fields(const char *template_fmt, { const char *template_fmt_ptr; struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX]; - int template_num_fields = template_fmt_size(template_fmt); + int template_num_fields; int i, len; + if (num_fields && *num_fields > 0) /* already initialized? */ + return 0; + + template_num_fields = template_fmt_size(template_fmt); + if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) { pr_err("format string '%s' contains too many fields\n", template_fmt); @@ -182,11 +200,28 @@ static int template_desc_init_fields(const char *template_fmt, return 0; } +void ima_init_template_list(void) +{ + int i; + + if (!list_empty(&defined_templates)) + return; + + spin_lock(&template_list); + for (i = 0; i < ARRAY_SIZE(builtin_templates); i++) { + list_add_tail_rcu(&builtin_templates[i].list, + &defined_templates); + } + spin_unlock(&template_list); +} + struct ima_template_desc *ima_template_desc_current(void) { - if (!ima_template) + if (!ima_template) { + ima_init_template_list(); ima_template = lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE); + } return ima_template; } @@ -205,3 +240,239 @@ int __init ima_init_template(void) return result; } + +static struct ima_template_desc *restore_template_fmt(char *template_name) +{ + struct ima_template_desc *template_desc = NULL; + int ret; + + ret = template_desc_init_fields(template_name, NULL, NULL); + if (ret < 0) { + pr_err("attempting to initialize the template \"%s\" failed\n", + template_name); + goto out; + } + + template_desc = kzalloc(sizeof(*template_desc), GFP_KERNEL); + if (!template_desc) + goto out; + + template_desc->name = ""; + template_desc->fmt = kstrdup(template_name, GFP_KERNEL); + if (!template_desc->fmt) + goto out; + + spin_lock(&template_list); + list_add_tail_rcu(&template_desc->list, &defined_templates); + spin_unlock(&template_list); +out: + return template_desc; +} + +static int ima_restore_template_data(struct ima_template_desc *template_desc, + void *template_data, + int template_data_size, + struct ima_template_entry **entry) +{ + struct binary_field_data { + u32 len; + u8 data[0]; + } __packed; + + struct binary_field_data *field_data; + int offset = 0; + int ret = 0; + int i; + + *entry = kzalloc(sizeof(**entry) + + template_desc->num_fields * sizeof(struct ima_field_data), + GFP_NOFS); + if (!*entry) + return -ENOMEM; + + (*entry)->template_desc = template_desc; + for (i = 0; i < template_desc->num_fields; i++) { + field_data = template_data + offset; + + /* Each field of the template data is prefixed with a length. */ + if (offset > (template_data_size - sizeof(*field_data))) { + pr_err("Restoring the template field failed\n"); + ret = -EINVAL; + break; + } + offset += sizeof(*field_data); + + if (ima_canonical_fmt) + field_data->len = le32_to_cpu(field_data->len); + + if (offset > (template_data_size - field_data->len)) { + pr_err("Restoring the template field data failed\n"); + ret = -EINVAL; + break; + } + offset += field_data->len; + + (*entry)->template_data[i].len = field_data->len; + (*entry)->template_data_len += sizeof(field_data->len); + + (*entry)->template_data[i].data = + kzalloc(field_data->len + 1, GFP_KERNEL); + if (!(*entry)->template_data[i].data) { + ret = -ENOMEM; + break; + } + memcpy((*entry)->template_data[i].data, field_data->data, + field_data->len); + (*entry)->template_data_len += field_data->len; + } + + if (ret < 0) { + ima_free_template_entry(*entry); + *entry = NULL; + } + + return ret; +} + +/* Restore the serialized binary measurement list without extending PCRs. */ +int ima_restore_measurement_list(loff_t size, void *buf) +{ + struct binary_hdr_v1 { + u32 pcr; + u8 digest[TPM_DIGEST_SIZE]; + u32 template_name_len; + char template_name[0]; + } __packed; + char template_name[MAX_TEMPLATE_NAME_LEN]; + + struct binary_data_v1 { + u32 template_data_size; + char template_data[0]; + } __packed; + + struct ima_kexec_hdr *khdr = buf; + struct binary_hdr_v1 *hdr_v1; + struct binary_data_v1 *data_v1; + + void *bufp = buf + sizeof(*khdr); + void *bufendp; + struct ima_template_entry *entry; + struct ima_template_desc *template_desc; + unsigned long count = 0; + int ret = 0; + + if (!buf || size < sizeof(*khdr)) + return 0; + + if (ima_canonical_fmt) { + khdr->version = le16_to_cpu(khdr->version); + khdr->count = le64_to_cpu(khdr->count); + khdr->buffer_size = le64_to_cpu(khdr->buffer_size); + } + + if (khdr->version != 1) { + pr_err("attempting to restore a incompatible measurement list"); + return -EINVAL; + } + + if (khdr->count > ULONG_MAX - 1) { + pr_err("attempting to restore too many measurements"); + return -EINVAL; + } + + /* + * ima kexec buffer prefix: version, buffer size, count + * v1 format: pcr, digest, template-name-len, template-name, + * template-data-size, template-data + */ + bufendp = buf + khdr->buffer_size; + while ((bufp < bufendp) && (count++ < khdr->count)) { + hdr_v1 = bufp; + if (bufp > (bufendp - sizeof(*hdr_v1))) { + pr_err("attempting to restore partial measurement\n"); + ret = -EINVAL; + break; + } + bufp += sizeof(*hdr_v1); + + if (ima_canonical_fmt) + hdr_v1->template_name_len = + le32_to_cpu(hdr_v1->template_name_len); + + if ((hdr_v1->template_name_len >= MAX_TEMPLATE_NAME_LEN) || + (bufp > (bufendp - hdr_v1->template_name_len))) { + pr_err("attempting to restore a template name \ + that is too long\n"); + ret = -EINVAL; + break; + } + data_v1 = bufp += (u_int8_t)hdr_v1->template_name_len; + + /* template name is not null terminated */ + memcpy(template_name, hdr_v1->template_name, + hdr_v1->template_name_len); + template_name[hdr_v1->template_name_len] = 0; + + if (strcmp(template_name, "ima") == 0) { + pr_err("attempting to restore an unsupported \ + template \"%s\" failed\n", template_name); + ret = -EINVAL; + break; + } + + template_desc = lookup_template_desc(template_name); + if (!template_desc) { + template_desc = restore_template_fmt(template_name); + if (!template_desc) + break; + } + + /* + * Only the running system's template format is initialized + * on boot. As needed, initialize the other template formats. + */ + ret = template_desc_init_fields(template_desc->fmt, + &(template_desc->fields), + &(template_desc->num_fields)); + if (ret < 0) { + pr_err("attempting to restore the template fmt \"%s\" \ + failed\n", template_desc->fmt); + ret = -EINVAL; + break; + } + + if (bufp > (bufendp - sizeof(data_v1->template_data_size))) { + pr_err("restoring the template data size failed\n"); + ret = -EINVAL; + break; + } + bufp += (u_int8_t) sizeof(data_v1->template_data_size); + + if (ima_canonical_fmt) + data_v1->template_data_size = + le32_to_cpu(data_v1->template_data_size); + + if (bufp > (bufendp - data_v1->template_data_size)) { + pr_err("restoring the template data failed\n"); + ret = -EINVAL; + break; + } + bufp += data_v1->template_data_size; + + ret = ima_restore_template_data(template_desc, + data_v1->template_data, + data_v1->template_data_size, + &entry); + if (ret < 0) + break; + + memcpy(entry->digest, hdr_v1->digest, TPM_DIGEST_SIZE); + entry->pcr = + !ima_canonical_fmt ? hdr_v1->pcr : le32_to_cpu(hdr_v1->pcr); + ret = ima_restore_measurement_entry(entry); + if (ret < 0) + break; + + } + return ret; +} diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index f9bae04ba176..f9ba37b3928d 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -103,8 +103,11 @@ static void ima_show_template_data_binary(struct seq_file *m, u32 len = (show == IMA_SHOW_BINARY_OLD_STRING_FMT) ? strlen(field_data->data) : field_data->len; - if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) - ima_putc(m, &len, sizeof(len)); + if (show != IMA_SHOW_BINARY_NO_FIELD_LEN) { + u32 field_len = !ima_canonical_fmt ? len : cpu_to_le32(len); + + ima_putc(m, &field_len, sizeof(field_len)); + } if (!len) return; diff --git a/security/keys/Kconfig b/security/keys/Kconfig index f826e8739023..d942c7c2bc0a 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -41,7 +41,7 @@ config BIG_KEYS bool "Large payload keys" depends on KEYS depends on TMPFS - select CRYPTO + depends on (CRYPTO_ANSI_CPRNG = y || CRYPTO_DRBG = y) select CRYPTO_AES select CRYPTO_ECB select CRYPTO_RNG diff --git a/security/keys/big_key.c b/security/keys/big_key.c index c0b3030b5634..835c1ab30d01 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -9,6 +9,7 @@ * 2 of the Licence, or (at your option) any later version. */ +#define pr_fmt(fmt) "big_key: "fmt #include <linux/init.h> #include <linux/seq_file.h> #include <linux/file.h> @@ -341,44 +342,48 @@ error: */ static int __init big_key_init(void) { - return register_key_type(&key_type_big_key); -} - -/* - * Initialize big_key crypto and RNG algorithms - */ -static int __init big_key_crypto_init(void) -{ - int ret = -EINVAL; + struct crypto_skcipher *cipher; + struct crypto_rng *rng; + int ret; - /* init RNG */ - big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0); - if (IS_ERR(big_key_rng)) { - big_key_rng = NULL; - return -EFAULT; + rng = crypto_alloc_rng(big_key_rng_name, 0, 0); + if (IS_ERR(rng)) { + pr_err("Can't alloc rng: %ld\n", PTR_ERR(rng)); + return PTR_ERR(rng); } + big_key_rng = rng; + /* seed RNG */ - ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng)); - if (ret) - goto error; + ret = crypto_rng_reset(rng, NULL, crypto_rng_seedsize(rng)); + if (ret) { + pr_err("Can't reset rng: %d\n", ret); + goto error_rng; + } /* init block cipher */ - big_key_skcipher = crypto_alloc_skcipher(big_key_alg_name, - 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(big_key_skcipher)) { - big_key_skcipher = NULL; - ret = -EFAULT; - goto error; + cipher = crypto_alloc_skcipher(big_key_alg_name, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(cipher)) { + ret = PTR_ERR(cipher); + pr_err("Can't alloc crypto: %d\n", ret); + goto error_rng; + } + + big_key_skcipher = cipher; + + ret = register_key_type(&key_type_big_key); + if (ret < 0) { + pr_err("Can't register type: %d\n", ret); + goto error_cipher; } return 0; -error: +error_cipher: + crypto_free_skcipher(big_key_skcipher); +error_rng: crypto_free_rng(big_key_rng); - big_key_rng = NULL; return ret; } -device_initcall(big_key_init); -late_initcall(big_key_crypto_init); +late_initcall(big_key_init); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index d580ad06b792..04a764f71ec8 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -23,7 +23,7 @@ #include <linux/vmalloc.h> #include <linux/security.h> #include <linux/uio.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "internal.h" #define KEY_MAX_DESC_SIZE 4096 @@ -1074,7 +1074,7 @@ long keyctl_instantiate_key_common(key_serial_t id, } ret = -EFAULT; - if (copy_from_iter(payload, plen, from) != plen) + if (!copy_from_iter_full(payload, plen, from)) goto error2; } diff --git a/security/keys/proc.c b/security/keys/proc.c index f0611a6368cd..b9f531c9e4fa 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -181,7 +181,7 @@ static int proc_keys_show(struct seq_file *m, void *v) struct timespec now; unsigned long timo; key_ref_t key_ref, skey_ref; - char xbuf[12]; + char xbuf[16]; int rc; struct keyring_search_context ctx = { diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 40a885239782..918cddcd4516 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -18,7 +18,7 @@ #include <linux/mutex.h> #include <linux/security.h> #include <linux/user_namespace.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "internal.h" /* Session keyring create vs join semaphore */ diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 9db8b4a82787..6bbe2f535f08 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -16,7 +16,7 @@ #include <linux/err.h> #include <linux/seq_file.h> #include <linux/slab.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "internal.h" #include <keys/user-type.h> diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 66b1840b4110..e187c8909d9d 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -15,7 +15,7 @@ #include <linux/seq_file.h> #include <linux/err.h> #include <keys/user-type.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "internal.h" static int logon_vet_description(const char *desc); diff --git a/security/lsm_audit.c b/security/lsm_audit.c index cccbf3068cdc..37f04dadc8d6 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -99,7 +99,7 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, } return ret; } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) /** * ipv6_skb_to_auditdata : fill auditdata from skb * @skb : the skb @@ -220,7 +220,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, */ BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); - audit_log_format(ab, " pid=%d comm=", task_pid_nr(current)); + audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current)); audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm))); switch (a->type) { @@ -245,6 +245,19 @@ static void dump_common_audit_data(struct audit_buffer *ab, } break; } + case LSM_AUDIT_DATA_FILE: { + struct inode *inode; + + audit_log_d_path(ab, " path=", &a->u.file->f_path); + + inode = file_inode(a->u.file); + if (inode) { + audit_log_format(ab, " dev="); + audit_log_untrustedstring(ab, inode->i_sb->s_id); + audit_log_format(ab, " ino=%lu", inode->i_ino); + } + break; + } case LSM_AUDIT_DATA_IOCTL_OP: { struct inode *inode; @@ -257,7 +270,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, audit_log_format(ab, " ino=%lu", inode->i_ino); } - audit_log_format(ab, " ioctlcmd=%hx", a->u.op->cmd); + audit_log_format(ab, " ioctlcmd=0x%hx", a->u.op->cmd); break; } case LSM_AUDIT_DATA_DENTRY: { @@ -294,7 +307,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, case LSM_AUDIT_DATA_TASK: { struct task_struct *tsk = a->u.tsk; if (tsk) { - pid_t pid = task_pid_nr(tsk); + pid_t pid = task_tgid_nr(tsk); if (pid) { char comm[sizeof(tsk->comm)]; audit_log_format(ab, " opid=%d ocomm=", pid); diff --git a/security/security.c b/security/security.c index 4838e7fefa1f..f825304f04a7 100644 --- a/security/security.c +++ b/security/security.c @@ -364,6 +364,15 @@ int security_dentry_init_security(struct dentry *dentry, int mode, } EXPORT_SYMBOL(security_dentry_init_security); +int security_dentry_create_files_as(struct dentry *dentry, int mode, + struct qstr *name, + const struct cred *old, struct cred *new) +{ + return call_int_hook(dentry_create_files_as, 0, dentry, mode, + name, old, new); +} +EXPORT_SYMBOL(security_dentry_create_files_as); + int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const initxattrs initxattrs, void *fs_data) @@ -748,6 +757,18 @@ void security_inode_getsecid(struct inode *inode, u32 *secid) call_void_hook(inode_getsecid, inode, secid); } +int security_inode_copy_up(struct dentry *src, struct cred **new) +{ + return call_int_hook(inode_copy_up, 0, src, new); +} +EXPORT_SYMBOL(security_inode_copy_up); + +int security_inode_copy_up_xattr(const char *name) +{ + return call_int_hook(inode_copy_up_xattr, -EOPNOTSUPP, name); +} +EXPORT_SYMBOL(security_inode_copy_up_xattr); + int security_file_permission(struct file *file, int mask) { int ret; @@ -1623,6 +1644,8 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.sb_parse_opts_str), .dentry_init_security = LIST_HEAD_INIT(security_hook_heads.dentry_init_security), + .dentry_create_files_as = + LIST_HEAD_INIT(security_hook_heads.dentry_create_files_as), #ifdef CONFIG_SECURITY_PATH .path_unlink = LIST_HEAD_INIT(security_hook_heads.path_unlink), .path_mkdir = LIST_HEAD_INIT(security_hook_heads.path_mkdir), @@ -1684,6 +1707,10 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.inode_listsecurity), .inode_getsecid = LIST_HEAD_INIT(security_hook_heads.inode_getsecid), + .inode_copy_up = + LIST_HEAD_INIT(security_hook_heads.inode_copy_up), + .inode_copy_up_xattr = + LIST_HEAD_INIT(security_hook_heads.inode_copy_up_xattr), .file_permission = LIST_HEAD_INIT(security_hook_heads.file_permission), .file_alloc_security = diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index 8691e92f27e5..ea7e3efbe0f7 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -93,41 +93,3 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE via /selinux/checkreqprot if authorized by policy. If you are unsure how to answer this question, answer 0. - -config SECURITY_SELINUX_POLICYDB_VERSION_MAX - bool "NSA SELinux maximum supported policy format version" - depends on SECURITY_SELINUX - default n - help - This option enables the maximum policy format version supported - by SELinux to be set to a particular value. This value is reported - to userspace via /selinux/policyvers and used at policy load time. - It can be adjusted downward to support legacy userland (init) that - does not correctly handle kernels that support newer policy versions. - - Examples: - For the Fedora Core 3 or 4 Linux distributions, enable this option - and set the value via the next option. For Fedora Core 5 and later, - do not enable this option. - - If you are unsure how to answer this question, answer N. - -config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE - int "NSA SELinux maximum supported policy format version value" - depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX - range 15 23 - default 19 - help - This option sets the value for the maximum policy format version - supported by SELinux. - - Examples: - For Fedora Core 3, use 18. - For Fedora Core 4, use 19. - - If you are unsure how to answer this question, look for the - policy format version supported by your policy toolchain, by - running 'checkpolicy -V'. Or look at what policy you have - installed under /etc/selinux/$SELINUXTYPE/policy, where - SELINUXTYPE is defined in your /etc/selinux/config. - diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 13185a6c266a..c7c6619431d5 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -231,12 +231,13 @@ static int inode_alloc_security(struct inode *inode) if (!isec) return -ENOMEM; - mutex_init(&isec->lock); + spin_lock_init(&isec->lock); INIT_LIST_HEAD(&isec->list); isec->inode = inode; isec->sid = SECINITSID_UNLABELED; isec->sclass = SECCLASS_FILE; isec->task_sid = sid; + isec->initialized = LABEL_INVALID; inode->i_security = isec; return 0; @@ -247,7 +248,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent /* * Try reloading inode security labels that have been marked as invalid. The * @may_sleep parameter indicates when sleeping and thus reloading labels is - * allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is + * allowed; when set to false, returns -ECHILD when the label is * invalid. The @opt_dentry parameter should be set to a dentry of the inode; * when no dentry is available, set it to NULL instead. */ @@ -507,14 +508,14 @@ static int sb_finish_set_opts(struct super_block *sb) the root directory. -ENODATA is ok, as this may be the first boot of the SELinux kernel before we have assigned xattr values to the filesystem. */ - if (!root_inode->i_op->getxattr) { + if (!(root_inode->i_opflags & IOP_XATTR)) { printk(KERN_WARNING "SELinux: (dev %s, type %s) has no " "xattr support\n", sb->s_id, sb->s_type->name); rc = -EOPNOTSUPP; goto out; } - rc = root_inode->i_op->getxattr(root, root_inode, - XATTR_NAME_SELINUX, NULL, 0); + + rc = __vfs_getxattr(root, root_inode, XATTR_NAME_SELINUX, NULL, 0); if (rc < 0 && rc != -ENODATA) { if (rc == -EOPNOTSUPP) printk(KERN_WARNING "SELinux: (dev %s, type " @@ -1100,11 +1101,12 @@ static int selinux_parse_opts_str(char *options, } rc = -ENOMEM; - opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_ATOMIC); + opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_KERNEL); if (!opts->mnt_opts) goto out_err; - opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC); + opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), + GFP_KERNEL); if (!opts->mnt_opts_flags) { kfree(opts->mnt_opts); goto out_err; @@ -1380,7 +1382,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent { struct superblock_security_struct *sbsec = NULL; struct inode_security_struct *isec = inode->i_security; - u32 sid; + u32 task_sid, sid = 0; + u16 sclass; struct dentry *dentry; #define INITCONTEXTLEN 255 char *context = NULL; @@ -1388,12 +1391,15 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent int rc = 0; if (isec->initialized == LABEL_INITIALIZED) - goto out; + return 0; - mutex_lock(&isec->lock); + spin_lock(&isec->lock); if (isec->initialized == LABEL_INITIALIZED) goto out_unlock; + if (isec->sclass == SECCLASS_FILE) + isec->sclass = inode_mode_to_security_class(inode->i_mode); + sbsec = inode->i_sb->s_security; if (!(sbsec->flags & SE_SBINITIALIZED)) { /* Defer initialization until selinux_complete_init, @@ -1406,15 +1412,20 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent goto out_unlock; } + sclass = isec->sclass; + task_sid = isec->task_sid; + sid = isec->sid; + isec->initialized = LABEL_PENDING; + spin_unlock(&isec->lock); + switch (sbsec->behavior) { case SECURITY_FS_USE_NATIVE: break; case SECURITY_FS_USE_XATTR: - if (!inode->i_op->getxattr) { - isec->sid = sbsec->def_sid; + if (!(inode->i_opflags & IOP_XATTR)) { + sid = sbsec->def_sid; break; } - /* Need a dentry, since the xattr API requires one. Life would be simpler if we could just pass the inode. */ if (opt_dentry) { @@ -1434,7 +1445,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent * inode_doinit with a dentry, before these inodes could * be used again by userspace. */ - goto out_unlock; + goto out; } len = INITCONTEXTLEN; @@ -1442,32 +1453,28 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent if (!context) { rc = -ENOMEM; dput(dentry); - goto out_unlock; + goto out; } context[len] = '\0'; - rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX, - context, len); + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); if (rc == -ERANGE) { kfree(context); /* Need a larger buffer. Query for the right size. */ - rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX, - NULL, 0); + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); if (rc < 0) { dput(dentry); - goto out_unlock; + goto out; } len = rc; context = kmalloc(len+1, GFP_NOFS); if (!context) { rc = -ENOMEM; dput(dentry); - goto out_unlock; + goto out; } context[len] = '\0'; - rc = inode->i_op->getxattr(dentry, inode, - XATTR_NAME_SELINUX, - context, len); + rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); } dput(dentry); if (rc < 0) { @@ -1476,7 +1483,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent "%d for dev=%s ino=%ld\n", __func__, -rc, inode->i_sb->s_id, inode->i_ino); kfree(context); - goto out_unlock; + goto out; } /* Map ENODATA to the default file SID */ sid = sbsec->def_sid; @@ -1506,29 +1513,25 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent } } kfree(context); - isec->sid = sid; break; case SECURITY_FS_USE_TASK: - isec->sid = isec->task_sid; + sid = task_sid; break; case SECURITY_FS_USE_TRANS: /* Default to the fs SID. */ - isec->sid = sbsec->sid; + sid = sbsec->sid; /* Try to obtain a transition SID. */ - isec->sclass = inode_mode_to_security_class(inode->i_mode); - rc = security_transition_sid(isec->task_sid, sbsec->sid, - isec->sclass, NULL, &sid); + rc = security_transition_sid(task_sid, sid, sclass, NULL, &sid); if (rc) - goto out_unlock; - isec->sid = sid; + goto out; break; case SECURITY_FS_USE_MNTPOINT: - isec->sid = sbsec->mntpoint_sid; + sid = sbsec->mntpoint_sid; break; default: /* Default to the fs superblock SID. */ - isec->sid = sbsec->sid; + sid = sbsec->sid; if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) { /* We must have a dentry to determine the label on @@ -1551,25 +1554,30 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent * could be used again by userspace. */ if (!dentry) - goto out_unlock; - isec->sclass = inode_mode_to_security_class(inode->i_mode); - rc = selinux_genfs_get_sid(dentry, isec->sclass, + goto out; + rc = selinux_genfs_get_sid(dentry, sclass, sbsec->flags, &sid); dput(dentry); if (rc) - goto out_unlock; - isec->sid = sid; + goto out; } break; } - isec->initialized = LABEL_INITIALIZED; +out: + spin_lock(&isec->lock); + if (isec->initialized == LABEL_PENDING) { + if (!sid || rc) { + isec->initialized = LABEL_INVALID; + goto out_unlock; + } + + isec->initialized = LABEL_INITIALIZED; + isec->sid = sid; + } out_unlock: - mutex_unlock(&isec->lock); -out: - if (isec->sclass == SECCLASS_FILE) - isec->sclass = inode_mode_to_security_class(inode->i_mode); + spin_unlock(&isec->lock); return rc; } @@ -1761,8 +1769,8 @@ static inline int file_path_has_perm(const struct cred *cred, { struct common_audit_data ad; - ad.type = LSM_AUDIT_DATA_PATH; - ad.u.path = file->f_path; + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; return inode_has_perm(cred, file_inode(file), av, &ad); } @@ -1784,8 +1792,8 @@ static int file_has_perm(const struct cred *cred, u32 sid = cred_sid(cred); int rc; - ad.type = LSM_AUDIT_DATA_PATH; - ad.u.path = file->f_path; + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; if (sid != fsec->sid) { rc = avc_has_perm(sid, fsec->sid, @@ -1808,13 +1816,13 @@ out: /* * Determine the label for an inode that might be unioned. */ -static int selinux_determine_inode_label(struct inode *dir, - const struct qstr *name, - u16 tclass, - u32 *_new_isid) +static int +selinux_determine_inode_label(const struct task_security_struct *tsec, + struct inode *dir, + const struct qstr *name, u16 tclass, + u32 *_new_isid) { const struct superblock_security_struct *sbsec = dir->i_sb->s_security; - const struct task_security_struct *tsec = current_security(); if ((sbsec->flags & SE_SBINITIALIZED) && (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) { @@ -1857,8 +1865,8 @@ static int may_create(struct inode *dir, if (rc) return rc; - rc = selinux_determine_inode_label(dir, &dentry->d_name, tclass, - &newsid); + rc = selinux_determine_inode_label(current_security(), dir, + &dentry->d_name, tclass, &newsid); if (rc) return rc; @@ -2365,8 +2373,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) new_tsec->sid = old_tsec->sid; } - ad.type = LSM_AUDIT_DATA_PATH; - ad.u.path = bprm->file->f_path; + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = bprm->file; if (new_tsec->sid == old_tsec->sid) { rc = avc_has_perm(old_tsec->sid, isec->sid, @@ -2530,7 +2538,8 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm) rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur); } task_unlock(current); - update_rlimit_cpu(current, rlimit(RLIMIT_CPU)); + if (IS_ENABLED(CONFIG_POSIX_TIMERS)) + update_rlimit_cpu(current, rlimit(RLIMIT_CPU)); } } @@ -2560,9 +2569,11 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm) */ rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL); if (rc) { - memset(&itimer, 0, sizeof itimer); - for (i = 0; i < 3; i++) - do_setitimer(i, &itimer, NULL); + if (IS_ENABLED(CONFIG_POSIX_TIMERS)) { + memset(&itimer, 0, sizeof itimer); + for (i = 0; i < 3; i++) + do_setitimer(i, &itimer, NULL); + } spin_lock_irq(¤t->sighand->siglock); if (!fatal_signal_pending(current)) { flush_sigqueue(¤t->pending); @@ -2838,7 +2849,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, u32 newsid; int rc; - rc = selinux_determine_inode_label(d_inode(dentry->d_parent), name, + rc = selinux_determine_inode_label(current_security(), + d_inode(dentry->d_parent), name, inode_mode_to_security_class(mode), &newsid); if (rc) @@ -2847,6 +2859,27 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, return security_sid_to_context(newsid, (char **)ctx, ctxlen); } +static int selinux_dentry_create_files_as(struct dentry *dentry, int mode, + struct qstr *name, + const struct cred *old, + struct cred *new) +{ + u32 newsid; + int rc; + struct task_security_struct *tsec; + + rc = selinux_determine_inode_label(old->security, + d_inode(dentry->d_parent), name, + inode_mode_to_security_class(mode), + &newsid); + if (rc) + return rc; + + tsec = new->security; + tsec->create_sid = newsid; + return 0; +} + static int selinux_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const char **name, @@ -2863,7 +2896,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, sid = tsec->sid; newsid = tsec->create_sid; - rc = selinux_determine_inode_label( + rc = selinux_determine_inode_label(current_security(), dir, qstr, inode_mode_to_security_class(inode->i_mode), &newsid); @@ -3178,9 +3211,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, } isec = backing_inode_security(dentry); + spin_lock(&isec->lock); isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; isec->initialized = LABEL_INITIALIZED; + spin_unlock(&isec->lock); return; } @@ -3273,9 +3308,11 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, if (rc) return rc; + spin_lock(&isec->lock); isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; isec->initialized = LABEL_INITIALIZED; + spin_unlock(&isec->lock); return 0; } @@ -3293,6 +3330,41 @@ static void selinux_inode_getsecid(struct inode *inode, u32 *secid) *secid = isec->sid; } +static int selinux_inode_copy_up(struct dentry *src, struct cred **new) +{ + u32 sid; + struct task_security_struct *tsec; + struct cred *new_creds = *new; + + if (new_creds == NULL) { + new_creds = prepare_creds(); + if (!new_creds) + return -ENOMEM; + } + + tsec = new_creds->security; + /* Get label from overlay inode and set it in create_sid */ + selinux_inode_getsecid(d_inode(src), &sid); + tsec->create_sid = sid; + *new = new_creds; + return 0; +} + +static int selinux_inode_copy_up_xattr(const char *name) +{ + /* The copy_up hook above sets the initial context on an inode, but we + * don't then want to overwrite it by blindly copying all the lower + * xattrs up. Instead, we have to filter out SELinux-related xattrs. + */ + if (strcmp(name, XATTR_NAME_SELINUX) == 0) + return 1; /* Discard */ + /* + * Any other attribute apart from SELINUX is not claimed, supported + * by selinux. + */ + return -EOPNOTSUPP; +} + /* file security operations */ static int selinux_revalidate_file_permission(struct file *file, int mask) @@ -3505,7 +3577,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, } else if (!vma->vm_file && ((vma->vm_start <= vma->vm_mm->start_stack && vma->vm_end >= vma->vm_mm->start_stack) || - vma_is_stack_for_task(vma, current))) { + vma_is_stack_for_current(vma))) { rc = current_has_perm(current, PROCESS__EXECSTACK); } else if (vma->vm_file && vma->anon_vma) { /* @@ -3776,8 +3848,8 @@ static int selinux_kernel_module_from_file(struct file *file) /* finit_module */ - ad.type = LSM_AUDIT_DATA_PATH; - ad.u.path = file->f_path; + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; fsec = file->f_security; if (sid != fsec->sid) { @@ -3901,8 +3973,11 @@ static void selinux_task_to_inode(struct task_struct *p, struct inode_security_struct *isec = inode->i_security; u32 sid = task_sid(p); + spin_lock(&isec->lock); + isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = sid; isec->initialized = LABEL_INITIALIZED; + spin_unlock(&isec->lock); } /* Returns error only if unable to parse addresses */ @@ -3984,7 +4059,7 @@ out: return ret; } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) /* Returns error only if unable to parse addresses */ static int selinux_parse_skb_ipv6(struct sk_buff *skb, @@ -4075,7 +4150,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad, &ad->u.net->v4info.daddr); goto okay; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) case PF_INET6: ret = selinux_parse_skb_ipv6(skb, ad, proto); if (ret) @@ -4221,24 +4296,24 @@ static int selinux_socket_post_create(struct socket *sock, int family, const struct task_security_struct *tsec = current_security(); struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); struct sk_security_struct *sksec; + u16 sclass = socket_type_to_security_class(family, type, protocol); + u32 sid = SECINITSID_KERNEL; int err = 0; - isec->sclass = socket_type_to_security_class(family, type, protocol); - - if (kern) - isec->sid = SECINITSID_KERNEL; - else { - err = socket_sockcreate_sid(tsec, isec->sclass, &(isec->sid)); + if (!kern) { + err = socket_sockcreate_sid(tsec, sclass, &sid); if (err) return err; } + isec->sclass = sclass; + isec->sid = sid; isec->initialized = LABEL_INITIALIZED; if (sock->sk) { sksec = sock->sk->sk_security; - sksec->sid = isec->sid; - sksec->sclass = isec->sclass; + sksec->sclass = sclass; + sksec->sid = sid; err = selinux_netlbl_socket_post_create(sock->sk, family); } @@ -4414,16 +4489,22 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) int err; struct inode_security_struct *isec; struct inode_security_struct *newisec; + u16 sclass; + u32 sid; err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); if (err) return err; - newisec = inode_security_novalidate(SOCK_INODE(newsock)); - isec = inode_security_novalidate(SOCK_INODE(sock)); - newisec->sclass = isec->sclass; - newisec->sid = isec->sid; + spin_lock(&isec->lock); + sclass = isec->sclass; + sid = isec->sid; + spin_unlock(&isec->lock); + + newisec = inode_security_novalidate(SOCK_INODE(newsock)); + newisec->sclass = sclass; + newisec->sid = sid; newisec->initialized = LABEL_INITIALIZED; return 0; @@ -5029,7 +5110,7 @@ static unsigned int selinux_ipv4_forward(void *priv, return selinux_ip_forward(skb, state->in, PF_INET); } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) static unsigned int selinux_ipv6_forward(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) @@ -5087,7 +5168,7 @@ static unsigned int selinux_ipv4_output(void *priv, return selinux_ip_output(skb, PF_INET); } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) static unsigned int selinux_ipv6_output(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) @@ -5273,7 +5354,7 @@ static unsigned int selinux_ipv4_postroute(void *priv, return selinux_ip_postroute(skb, state->out, PF_INET); } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) static unsigned int selinux_ipv6_postroute(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) @@ -5926,9 +6007,9 @@ static void selinux_inode_invalidate_secctx(struct inode *inode) { struct inode_security_struct *isec = inode->i_security; - mutex_lock(&isec->lock); + spin_lock(&isec->lock); isec->initialized = LABEL_INVALID; - mutex_unlock(&isec->lock); + spin_unlock(&isec->lock); } /* @@ -6062,6 +6143,7 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(sb_parse_opts_str, selinux_parse_opts_str), LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security), + LSM_HOOK_INIT(dentry_create_files_as, selinux_dentry_create_files_as), LSM_HOOK_INIT(inode_alloc_security, selinux_inode_alloc_security), LSM_HOOK_INIT(inode_free_security, selinux_inode_free_security), @@ -6088,6 +6170,8 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity), LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity), LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid), + LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up), + LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr), LSM_HOOK_INIT(file_permission, selinux_file_permission), LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), @@ -6317,7 +6401,7 @@ static struct nf_hook_ops selinux_nf_ops[] = { .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_SELINUX_FIRST, }, -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) { .hook = selinux_ipv6_postroute, .pf = NFPROTO_IPV6, diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 1f1f4b2f6018..13ae49b0baa0 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -1,3 +1,5 @@ +#include <linux/capability.h> + #define COMMON_FILE_SOCK_PERMS "ioctl", "read", "write", "create", \ "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" @@ -24,6 +26,10 @@ #define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \ "wake_alarm", "block_suspend", "audit_read" +#if CAP_LAST_CAP > CAP_AUDIT_READ +#error New capability defined, please update COMMON_CAP2_PERMS. +#endif + /* * Note: The name for any socket class should be suffixed by "socket", * and doesn't contain more than one substr of "socket". diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index c21e135460a5..e8dab0f02c72 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -39,7 +39,8 @@ struct task_security_struct { enum label_initialized { LABEL_INVALID, /* invalid or not initialized */ - LABEL_INITIALIZED /* initialized */ + LABEL_INITIALIZED, /* initialized */ + LABEL_PENDING }; struct inode_security_struct { @@ -52,7 +53,7 @@ struct inode_security_struct { u32 sid; /* SID of this object */ u16 sclass; /* security class of this object */ unsigned char initialized; /* initialization flag */ - struct mutex lock; + spinlock_t lock; }; struct file_security_struct { diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 38feb55d531a..308a286c6cbe 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -39,11 +39,7 @@ /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX -#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE -#else #define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL -#endif /* Mask for just the mount related flags */ #define SE_MNTMASK 0x0f diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 0765c5b053b5..cf9293e01fc1 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -163,6 +163,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, if (sscanf(page, "%d", &new_value) != 1) goto out; + new_value = !!new_value; + if (new_value != selinux_enforcing) { length = task_has_security(current, SECURITY__SETENFORCE); if (length) @@ -1089,7 +1091,7 @@ static struct inode *sel_make_inode(struct super_block *sb, int mode) if (ret) { ret->i_mode = mode; - ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME; + ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret); } return ret; } @@ -1301,7 +1303,7 @@ static int sel_make_bools(void) goto out; isec->sid = sid; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; inode->i_fop = &sel_bool_ops; inode->i_ino = i|SEL_BOOL_INO_OFFSET; d_add(dentry, inode); @@ -1834,7 +1836,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) isec = (struct inode_security_struct *)inode->i_security; isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; - isec->initialized = 1; + isec->initialized = LABEL_INITIALIZED; init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); d_add(dentry, inode); diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 456e1a9bcfde..34afeadd9e73 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -242,6 +242,8 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) goto err; len = le32_to_cpu(buf[2]); + if (((len == 0) || (len == (u32)-1))) + goto err; rc = -ENOMEM; key = kmalloc(len + 1, GFP_KERNEL); diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 894b6cdc11c5..7d10e5d418bb 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -374,6 +374,9 @@ int ebitmap_read(struct ebitmap *e, void *fp) goto ok; } + if (e->highbit && !count) + goto bad; + for (i = 0; i < count; i++) { rc = next_entry(&startbit, fp, sizeof(u32)); if (rc < 0) { diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 992a31530825..d719db4219cd 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -527,9 +527,9 @@ static int policydb_index(struct policydb *p) printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools", p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim); if (p->mls_enabled) - printk(", %d sens, %d cats", p->p_levels.nprim, + printk(KERN_CONT ", %d sens, %d cats", p->p_levels.nprim, p->p_cats.nprim); - printk("\n"); + printk(KERN_CONT "\n"); printk(KERN_DEBUG "SELinux: %d classes, %d rules\n", p->p_classes.nprim, p->te_avtab.nel); @@ -541,21 +541,21 @@ static int policydb_index(struct policydb *p) rc = -ENOMEM; p->class_val_to_struct = - kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), + kzalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL); if (!p->class_val_to_struct) goto out; rc = -ENOMEM; p->role_val_to_struct = - kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), + kzalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), GFP_KERNEL); if (!p->role_val_to_struct) goto out; rc = -ENOMEM; p->user_val_to_struct = - kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), + kzalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), GFP_KERNEL); if (!p->user_val_to_struct) goto out; @@ -964,7 +964,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c) * Role must be authorized for the type. */ role = p->role_val_to_struct[c->role - 1]; - if (!ebitmap_get_bit(&role->types, c->type - 1)) + if (!role || !ebitmap_get_bit(&role->types, c->type - 1)) /* role may not be associated with type */ return 0; @@ -1094,6 +1094,9 @@ static int str_read(char **strp, gfp_t flags, void *fp, u32 len) int rc; char *str; + if ((len == 0) || (len == (u32)-1)) + return -EINVAL; + str = kmalloc(len + 1, flags); if (!str) return -ENOMEM; @@ -2414,6 +2417,7 @@ int policydb_read(struct policydb *p, void *fp) } else tr->tclass = p->process_class; + rc = -EINVAL; if (!policydb_role_isvalid(p, tr->role) || !policydb_type_isvalid(p, tr->type) || !policydb_class_isvalid(p, tr->tclass) || diff --git a/security/smack/Kconfig b/security/smack/Kconfig index 271adae81796..923b120e0fa5 100644 --- a/security/smack/Kconfig +++ b/security/smack/Kconfig @@ -40,3 +40,15 @@ config SECURITY_SMACK_NETFILTER This enables security marking of network packets using Smack labels. If you are unsure how to answer this question, answer N. + +config SECURITY_SMACK_APPEND_SIGNALS + bool "Treat delivering signals as an append operation" + depends on SECURITY_SMACK + default n + help + Sending a signal has been treated as a write operation to the + receiving process. If this option is selected, the delivery + will be an append operation instead. This makes it possible + to differentiate between delivering a network packet and + delivering a signal in the Smack rules. + If you are unsure how to answer this question, answer N. diff --git a/security/smack/smack.h b/security/smack/smack.h index 26e58f1804b1..77abe2efacae 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -256,6 +256,16 @@ enum { #define MAY_LOCK 0x00002000 /* Locks should be writes, but ... */ #define MAY_BRINGUP 0x00004000 /* Report use of this rule */ +/* + * The policy for delivering signals is configurable. + * It is usually "write", but can be "append". + */ +#ifdef CONFIG_SECURITY_SMACK_APPEND_SIGNALS +#define MAY_DELIVER MAY_APPEND /* Signal delivery requires append */ +#else +#define MAY_DELIVER MAY_WRITE /* Signal delivery requires write */ +#endif + #define SMACK_BRINGUP_ALLOW 1 /* Allow bringup mode */ #define SMACK_UNCONFINED_SUBJECT 2 /* Allow unconfined label */ #define SMACK_UNCONFINED_OBJECT 3 /* Allow unconfined label */ @@ -326,7 +336,6 @@ extern int smack_ptrace_rule; extern struct smack_known smack_known_floor; extern struct smack_known smack_known_hat; extern struct smack_known smack_known_huh; -extern struct smack_known smack_known_invalid; extern struct smack_known smack_known_star; extern struct smack_known smack_known_web; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 23e5808a0970..356e3764cad9 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -36,11 +36,6 @@ struct smack_known smack_known_floor = { .smk_secid = 5, }; -struct smack_known smack_known_invalid = { - .smk_known = "", - .smk_secid = 6, -}; - struct smack_known smack_known_web = { .smk_known = "@", .smk_secid = 7, @@ -615,7 +610,7 @@ struct smack_known *smack_from_secid(const u32 secid) * of a secid that is not on the list. */ rcu_read_unlock(); - return &smack_known_invalid; + return &smack_known_huh; } /* diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 87a9741b0d02..94dc9d406ce3 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -225,7 +225,7 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file, { struct task_smack *tsp = cred->security; struct smack_known *sskp = tsp->smk_task; - struct inode *inode = file->f_inode; + struct inode *inode = file_inode(file); struct inode_smack *isp = inode->i_security; char acc[SMK_NUM_ACCESS_TYPE + 1]; @@ -265,14 +265,14 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip, char *buffer; struct smack_known *skp = NULL; - if (ip->i_op->getxattr == NULL) + if (!(ip->i_opflags & IOP_XATTR)) return ERR_PTR(-EOPNOTSUPP); buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL); if (buffer == NULL) return ERR_PTR(-ENOMEM); - rc = ip->i_op->getxattr(dp, ip, name, buffer, SMK_LONGLABEL); + rc = __vfs_getxattr(dp, ip, name, buffer, SMK_LONGLABEL); if (rc < 0) skp = ERR_PTR(rc); else if (rc == 0) @@ -692,12 +692,12 @@ static int smack_parse_opts_str(char *options, } } - opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_ATOMIC); + opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL); if (!opts->mnt_opts) goto out_err; opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int), - GFP_ATOMIC); + GFP_KERNEL); if (!opts->mnt_opts_flags) { kfree(opts->mnt_opts); goto out_err; @@ -769,6 +769,31 @@ static int smack_set_mnt_opts(struct super_block *sb, if (sp->smk_flags & SMK_SB_INITIALIZED) return 0; + if (!smack_privileged(CAP_MAC_ADMIN)) { + /* + * Unprivileged mounts don't get to specify Smack values. + */ + if (num_opts) + return -EPERM; + /* + * Unprivileged mounts get root and default from the caller. + */ + skp = smk_of_current(); + sp->smk_root = skp; + sp->smk_default = skp; + /* + * For a handful of fs types with no user-controlled + * backing store it's okay to trust security labels + * in the filesystem. The rest are untrusted. + */ + if (sb->s_user_ns != &init_user_ns && + sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC && + sb->s_magic != RAMFS_MAGIC) { + transmute = 1; + sp->smk_flags |= SMK_SB_UNTRUSTED; + } + } + sp->smk_flags |= SMK_SB_INITIALIZED; for (i = 0; i < num_opts; i++) { @@ -809,31 +834,6 @@ static int smack_set_mnt_opts(struct super_block *sb, } } - if (!smack_privileged(CAP_MAC_ADMIN)) { - /* - * Unprivileged mounts don't get to specify Smack values. - */ - if (num_opts) - return -EPERM; - /* - * Unprivileged mounts get root and default from the caller. - */ - skp = smk_of_current(); - sp->smk_root = skp; - sp->smk_default = skp; - /* - * For a handful of fs types with no user-controlled - * backing store it's okay to trust security labels - * in the filesystem. The rest are untrusted. - */ - if (sb->s_user_ns != &init_user_ns && - sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC && - sb->s_magic != RAMFS_MAGIC) { - transmute = 1; - sp->smk_flags |= SMK_SB_UNTRUSTED; - } - } - /* * Initialize the root inode. */ @@ -1384,20 +1384,14 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, skp = smk_import_entry(value, size); if (!IS_ERR(skp)) isp->smk_inode = skp; - else - isp->smk_inode = &smack_known_invalid; } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { skp = smk_import_entry(value, size); if (!IS_ERR(skp)) isp->smk_task = skp; - else - isp->smk_task = &smack_known_invalid; } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { skp = smk_import_entry(value, size); if (!IS_ERR(skp)) isp->smk_mmap = skp; - else - isp->smk_mmap = &smack_known_invalid; } return; @@ -1857,14 +1851,14 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, /* we don't log here as rc can be overriden */ skp = file->f_security; - rc = smk_access(skp, tkp, MAY_WRITE, NULL); - rc = smk_bu_note("sigiotask", skp, tkp, MAY_WRITE, rc); + rc = smk_access(skp, tkp, MAY_DELIVER, NULL); + rc = smk_bu_note("sigiotask", skp, tkp, MAY_DELIVER, 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(skp->smk_known, tkp->smk_known, MAY_WRITE, rc, &ad); + smack_log(skp->smk_known, tkp->smk_known, MAY_DELIVER, rc, &ad); return rc; } @@ -2023,6 +2017,8 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, if (new_tsp == NULL) return -ENOMEM; + new->security = new_tsp; + rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp); if (rc != 0) return rc; @@ -2032,7 +2028,6 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, if (rc != 0) return rc; - new->security = new_tsp; return 0; } @@ -2067,12 +2062,8 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old) static int smack_kernel_act_as(struct cred *new, u32 secid) { struct task_smack *new_tsp = new->security; - struct smack_known *skp = smack_from_secid(secid); - - if (skp == NULL) - return -EINVAL; - new_tsp->smk_task = skp; + new_tsp->smk_task = smack_from_secid(secid); return 0; } @@ -2265,8 +2256,8 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * can write the receiver. */ if (secid == 0) { - rc = smk_curacc(tkp, MAY_WRITE, &ad); - rc = smk_bu_task(p, MAY_WRITE, rc); + rc = smk_curacc(tkp, MAY_DELIVER, &ad); + rc = smk_bu_task(p, MAY_DELIVER, rc); return rc; } /* @@ -2275,8 +2266,8 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * we can't take privilege into account. */ skp = smack_from_secid(secid); - rc = smk_access(skp, tkp, MAY_WRITE, &ad); - rc = smk_bu_note("USB signal", skp, tkp, MAY_WRITE, rc); + rc = smk_access(skp, tkp, MAY_DELIVER, &ad); + rc = smk_bu_note("USB signal", skp, tkp, MAY_DELIVER, rc); return rc; } @@ -2337,8 +2328,16 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) if (ssp == NULL) return -ENOMEM; - ssp->smk_in = skp; - ssp->smk_out = skp; + /* + * Sockets created by kernel threads receive web label. + */ + if (unlikely(current->flags & PF_KTHREAD)) { + ssp->smk_in = &smack_known_web; + ssp->smk_out = &smack_known_web; + } else { + ssp->smk_in = skp; + ssp->smk_out = skp; + } ssp->smk_packet = NULL; sk->sk_security = ssp; @@ -2435,17 +2434,17 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip) list_for_each_entry_rcu(snp, &smk_net6addr_list, list) { /* + * If the label is NULL the entry has + * been renounced. Ignore it. + */ + if (snp->smk_label == NULL) + continue; + /* * we break after finding the first match because * the list is sorted from longest to shortest mask * so we have found the most specific match */ for (found = 1, i = 0; i < 8; i++) { - /* - * If the label is NULL the entry has - * been renounced. Ignore it. - */ - if (snp->smk_label == NULL) - continue; if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) != snp->smk_host.s6_addr16[i]) { found = 0; @@ -3520,8 +3519,8 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * It would be curious if the label of the task * does not match that assigned. */ - if (inode->i_op->getxattr == NULL) - break; + if (!(inode->i_opflags & IOP_XATTR)) + break; /* * Get the dentry for xattr. */ @@ -3545,12 +3544,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ if (isp->smk_flags & SMK_INODE_CHANGED) { isp->smk_flags &= ~SMK_INODE_CHANGED; - rc = inode->i_op->setxattr(dp, inode, + rc = __vfs_setxattr(dp, inode, XATTR_NAME_SMACKTRANSMUTE, TRANS_TRUE, TRANS_TRUE_SIZE, 0); } else { - rc = inode->i_op->getxattr(dp, inode, + rc = __vfs_getxattr(dp, inode, XATTR_NAME_SMACKTRANSMUTE, trattr, TRANS_TRUE_SIZE); if (rc >= 0 && strncmp(trattr, TRANS_TRUE, @@ -3661,10 +3660,11 @@ static int smack_setprocattr(struct task_struct *p, char *name, return PTR_ERR(skp); /* - * No process is ever allowed the web ("@") label. + * No process is ever allowed the web ("@") label + * and the star ("*") label. */ - if (skp == &smack_known_web) - return -EPERM; + if (skp == &smack_known_web || skp == &smack_known_star) + return -EINVAL; if (!smack_privileged(CAP_MAC_ADMIN)) { rc = -EPERM; @@ -3884,21 +3884,11 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, return &smack_known_web; return &smack_known_star; } - if ((sap->flags & NETLBL_SECATTR_SECID) != 0) { + if ((sap->flags & NETLBL_SECATTR_SECID) != 0) /* * Looks like a fallback, which gives us a secid. */ - skp = smack_from_secid(sap->attr.secid); - /* - * This has got to be a bug because it is - * impossible to specify a fallback without - * specifying the label, which will ensure - * it has a secid, and the only way to get a - * secid is from a fallback. - */ - BUG_ON(skp == NULL); - return skp; - } + return smack_from_secid(sap->attr.secid); /* * Without guidance regarding the smack value * for the packet fall back on the network @@ -4761,7 +4751,6 @@ static __init void init_smack_known_list(void) mutex_init(&smack_known_hat.smk_rules_lock); mutex_init(&smack_known_floor.smk_rules_lock); mutex_init(&smack_known_star.smk_rules_lock); - mutex_init(&smack_known_invalid.smk_rules_lock); mutex_init(&smack_known_web.smk_rules_lock); /* * Initialize rule lists @@ -4770,7 +4759,6 @@ static __init void init_smack_known_list(void) INIT_LIST_HEAD(&smack_known_hat.smk_rules); INIT_LIST_HEAD(&smack_known_star.smk_rules); INIT_LIST_HEAD(&smack_known_floor.smk_rules); - INIT_LIST_HEAD(&smack_known_invalid.smk_rules); INIT_LIST_HEAD(&smack_known_web.smk_rules); /* * Create the known labels list @@ -4779,7 +4767,6 @@ static __init void init_smack_known_list(void) smk_insert_entry(&smack_known_hat); smk_insert_entry(&smack_known_star); smk_insert_entry(&smack_known_floor); - smk_insert_entry(&smack_known_invalid); smk_insert_entry(&smack_known_web); } diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c index aa6bf1b22ec5..205b785fb400 100644 --- a/security/smack/smack_netfilter.c +++ b/security/smack/smack_netfilter.c @@ -20,7 +20,7 @@ #include <net/inet_sock.h> #include "smack.h" -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) static unsigned int smack_ipv6_output(void *priv, struct sk_buff *skb, @@ -64,7 +64,7 @@ static struct nf_hook_ops smack_nf_ops[] = { .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_SELINUX_FIRST, }, -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if IS_ENABLED(CONFIG_IPV6) { .hook = smack_ipv6_output, .pf = NFPROTO_IPV6, diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index e249a66db533..13743a01b35b 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -2523,14 +2523,9 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, if (count == 0 || count > SMK_LONGLABEL) return -EINVAL; - data = kzalloc(count, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, buf, count) != 0) { - rc = -EFAULT; - goto out_data; - } + data = memdup_user(buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); cp = smk_parse_smack(data, count); if (IS_ERR(cp)) { @@ -3003,9 +2998,6 @@ static int __init init_smk_fs(void) rc = smk_preset_netlabel(&smack_known_huh); if (err == 0 && rc < 0) err = rc; - rc = smk_preset_netlabel(&smack_known_invalid); - if (err == 0 && rc < 0) - err = rc; rc = smk_preset_netlabel(&smack_known_star); if (err == 0 && rc < 0) err = rc; diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index ade7c6cad172..838ffa78cfda 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -881,7 +881,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, * the execve(). */ if (get_user_pages_remote(current, bprm->mm, pos, 1, - 0, 1, &page, NULL) <= 0) + FOLL_FORCE, &page, NULL, NULL) <= 0) return false; #else page = bprm->page[pos / PAGE_SIZE]; diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 5077f1968841..a97b275ca3af 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 && !inode->i_op->rename2) + if (!inode->i_op->rename) goto prepend_filesystem_name; } /* Prepend device name. */ @@ -283,7 +283,7 @@ char *tomoyo_realpath_from_path(const struct path *path) * or dentry without vfsmount. */ if (!path->mnt || - (!inode->i_op->rename && !inode->i_op->rename2)) + (!inode->i_op->rename)) pos = tomoyo_get_local_path(path->dentry, buf, buf_len - 1); /* Get absolute name for the rest. */ diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 0309f2111c70..968e5e0a3f81 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -309,7 +309,7 @@ static int task_is_descendant(struct task_struct *parent, * @tracer: the task_struct of the process attempting ptrace * @tracee: the task_struct of the process to be ptraced * - * Returns 1 if tracer has is ptracer exception ancestor for tracee. + * Returns 1 if tracer has a ptracer exception ancestor for tracee. */ static int ptracer_exception_found(struct task_struct *tracer, struct task_struct *tracee) @@ -320,6 +320,18 @@ static int ptracer_exception_found(struct task_struct *tracer, bool found = false; rcu_read_lock(); + + /* + * If there's already an active tracing relationship, then make an + * exception for the sake of other accesses, like process_vm_rw(). + */ + parent = ptrace_parent(tracee); + if (parent != NULL && same_thread_group(parent, tracer)) { + rc = 1; + goto unlock; + } + + /* Look for a PR_SET_PTRACER relationship. */ if (!thread_group_leader(tracee)) tracee = rcu_dereference(tracee->group_leader); list_for_each_entry_rcu(relation, &ptracer_relations, node) { @@ -334,6 +346,8 @@ static int ptracer_exception_found(struct task_struct *tracer, if (found && (parent == NULL || task_is_descendant(parent, tracer))) rc = 1; + +unlock: rcu_read_unlock(); return rc; |