diff options
Diffstat (limited to 'security')
33 files changed, 451 insertions, 208 deletions
diff --git a/security/Kconfig b/security/Kconfig index e8e449444e65..b0cb9a5f9448 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -54,6 +54,17 @@ config SECURITY_NETWORK implement socket and networking access controls. If you are unsure how to answer this question, answer N. +config PAGE_TABLE_ISOLATION + bool "Remove the kernel mapping in user mode" + default y + depends on X86_64 && !UML + help + This feature reduces the number of hardware side channels by + ensuring that the majority of kernel addresses are not mapped + into userspace. + + See Documentation/x86/pti.txt for more details. + config SECURITY_INFINIBAND bool "Infiniband Security Hooks" depends on SECURITY && INFINIBAND diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d4fa04d91439..4d202b73a0e1 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -571,10 +571,10 @@ static int ns_revision_open(struct inode *inode, struct file *file) return 0; } -static unsigned int ns_revision_poll(struct file *file, poll_table *pt) +static __poll_t ns_revision_poll(struct file *file, poll_table *pt) { struct aa_revision *rev = file->private_data; - unsigned int mask = 0; + __poll_t mask = 0; if (rev) { mutex_lock_nested(&rev->ns->lock, rev->ns->level); diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 04ba9d0718ea..6a54d2ffa840 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -330,10 +330,7 @@ static struct aa_profile *__attach_match(const char *name, continue; if (profile->xmatch) { - if (profile->xmatch_len == len) { - conflict = true; - continue; - } else if (profile->xmatch_len > len) { + if (profile->xmatch_len >= len) { unsigned int state; u32 perm; @@ -342,6 +339,10 @@ static struct aa_profile *__attach_match(const char *name, perm = dfa_user_allow(profile->xmatch, state); /* any accepting state means a valid match. */ if (perm & MAY_EXEC) { + if (profile->xmatch_len == len) { + conflict = true; + continue; + } candidate = profile; len = profile->xmatch_len; conflict = false; diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index 2b27bb79aec4..d7b7e7115160 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h @@ -133,6 +133,9 @@ extern struct aa_perms allperms; #define xcheck_labels_profiles(L1, L2, FN, args...) \ xcheck_ns_labels((L1), (L2), xcheck_ns_profile_label, (FN), args) +#define xcheck_labels(L1, L2, P, FN1, FN2) \ + xcheck(fn_for_each((L1), (P), (FN1)), fn_for_each((L2), (P), (FN2))) + void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask); void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask); diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 7ca0032e7ba9..b40678f3c1d5 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -64,40 +64,48 @@ static void audit_ptrace_cb(struct audit_buffer *ab, void *va) FLAGS_NONE, GFP_ATOMIC); } +/* assumes check for PROFILE_MEDIATES is already done */ /* TODO: conditionals */ static int profile_ptrace_perm(struct aa_profile *profile, - struct aa_profile *peer, u32 request, - struct common_audit_data *sa) + struct aa_label *peer, u32 request, + struct common_audit_data *sa) { struct aa_perms perms = { }; - /* need because of peer in cross check */ - if (profile_unconfined(profile) || - !PROFILE_MEDIATES(profile, AA_CLASS_PTRACE)) - return 0; - - aad(sa)->peer = &peer->label; - aa_profile_match_label(profile, &peer->label, AA_CLASS_PTRACE, request, + aad(sa)->peer = peer; + aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request, &perms); aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); } -static int cross_ptrace_perm(struct aa_profile *tracer, - struct aa_profile *tracee, u32 request, - struct common_audit_data *sa) +static int profile_tracee_perm(struct aa_profile *tracee, + struct aa_label *tracer, u32 request, + struct common_audit_data *sa) { + if (profile_unconfined(tracee) || unconfined(tracer) || + !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE)) + return 0; + + return profile_ptrace_perm(tracee, tracer, request, sa); +} + +static int profile_tracer_perm(struct aa_profile *tracer, + struct aa_label *tracee, u32 request, + struct common_audit_data *sa) +{ + if (profile_unconfined(tracer)) + return 0; + if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) - return xcheck(profile_ptrace_perm(tracer, tracee, request, sa), - profile_ptrace_perm(tracee, tracer, - request << PTRACE_PERM_SHIFT, - sa)); - /* policy uses the old style capability check for ptrace */ - if (profile_unconfined(tracer) || tracer == tracee) + return profile_ptrace_perm(tracer, tracee, request, sa); + + /* profile uses the old style capability check for ptrace */ + if (&tracer->label == tracee) return 0; aad(sa)->label = &tracer->label; - aad(sa)->peer = &tracee->label; + aad(sa)->peer = tracee; aad(sa)->request = 0; aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1); @@ -115,10 +123,13 @@ static int cross_ptrace_perm(struct aa_profile *tracer, int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, u32 request) { + struct aa_profile *profile; + u32 xrequest = request << PTRACE_PERM_SHIFT; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); - return xcheck_labels_profiles(tracer, tracee, cross_ptrace_perm, - request, &sa); + return xcheck_labels(tracer, tracee, profile, + profile_tracer_perm(profile, tracee, request, &sa), + profile_tracee_perm(profile, tracer, xrequest, &sa)); } diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index ed9b4d0f9f7e..8c558cbce930 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -329,6 +329,9 @@ static int match_mnt_path_str(struct aa_profile *profile, AA_BUG(!mntpath); AA_BUG(!buffer); + if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) + return 0; + error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer, &mntpnt, &info, profile->disconnected); if (error) @@ -380,6 +383,9 @@ static int match_mnt(struct aa_profile *profile, const struct path *path, AA_BUG(!profile); AA_BUG(devpath && !devbuffer); + if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) + return 0; + if (devpath) { error = aa_path_name(devpath, path_flags(profile, devpath), devbuffer, &devname, &info, @@ -558,6 +564,9 @@ static int profile_umount(struct aa_profile *profile, struct path *path, AA_BUG(!profile); AA_BUG(!path); + if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) + return 0; + error = aa_path_name(path, path_flags(profile, path), buffer, &name, &info, profile->disconnected); if (error) @@ -613,7 +622,8 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile, AA_BUG(!new_path); AA_BUG(!old_path); - if (profile_unconfined(profile)) + if (profile_unconfined(profile) || + !PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) return aa_get_newest_label(&profile->label); error = aa_path_name(old_path, path_flags(profile, old_path), diff --git a/security/commoncap.c b/security/commoncap.c index 4f8e09340956..48620c93d697 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -348,21 +348,18 @@ static __u32 sansflags(__u32 m) return m & ~VFS_CAP_FLAGS_EFFECTIVE; } -static bool is_v2header(size_t size, __le32 magic) +static bool is_v2header(size_t size, const struct vfs_cap_data *cap) { - __u32 m = le32_to_cpu(magic); if (size != XATTR_CAPS_SZ_2) return false; - return sansflags(m) == VFS_CAP_REVISION_2; + return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_2; } -static bool is_v3header(size_t size, __le32 magic) +static bool is_v3header(size_t size, const struct vfs_cap_data *cap) { - __u32 m = le32_to_cpu(magic); - if (size != XATTR_CAPS_SZ_3) return false; - return sansflags(m) == VFS_CAP_REVISION_3; + return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_3; } /* @@ -405,7 +402,7 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, fs_ns = inode->i_sb->s_user_ns; cap = (struct vfs_cap_data *) tmpbuf; - if (is_v2header((size_t) ret, cap->magic_etc)) { + if (is_v2header((size_t) ret, cap)) { /* If this is sizeof(vfs_cap_data) then we're ok with the * on-disk value, so return that. */ if (alloc) @@ -413,7 +410,7 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, else kfree(tmpbuf); return ret; - } else if (!is_v3header((size_t) ret, cap->magic_etc)) { + } else if (!is_v3header((size_t) ret, cap)) { kfree(tmpbuf); return -EINVAL; } @@ -470,9 +467,9 @@ static kuid_t rootid_from_xattr(const void *value, size_t size, return make_kuid(task_ns, rootid); } -static bool validheader(size_t size, __le32 magic) +static bool validheader(size_t size, const struct vfs_cap_data *cap) { - return is_v2header(size, magic) || is_v3header(size, magic); + return is_v2header(size, cap) || is_v3header(size, cap); } /* @@ -495,7 +492,7 @@ int cap_convert_nscap(struct dentry *dentry, void **ivalue, size_t size) if (!*ivalue) return -EINVAL; - if (!validheader(size, cap->magic_etc)) + if (!validheader(size, cap)) return -EINVAL; if (!capable_wrt_inode_uidgid(inode, CAP_SETFCAP)) return -EPERM; diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 241aca315b0c..04825393facb 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -23,9 +23,12 @@ #define EVM_INIT_HMAC 0x0001 #define EVM_INIT_X509 0x0002 -#define EVM_SETUP 0x80000000 /* userland has signaled key load */ +#define EVM_ALLOW_METADATA_WRITES 0x0004 +#define EVM_SETUP_COMPLETE 0x80000000 /* userland has signaled key load */ -#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP) +#define EVM_KEY_MASK (EVM_INIT_HMAC | EVM_INIT_X509) +#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \ + EVM_ALLOW_METADATA_WRITES) extern int evm_initialized; extern char *evm_hmac; @@ -51,7 +54,7 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, size_t req_xattr_value_len, char *digest); int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, const char *req_xattr_value, - size_t req_xattr_value_len, char *digest); + size_t req_xattr_value_len, char type, char *digest); int evm_init_hmac(struct inode *inode, const struct xattr *xattr, char *hmac_val); int evm_init_secfs(void); diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index bcd64baf8788..691f3e09154c 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -138,7 +138,7 @@ out: * protection.) */ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, - char *digest) + char type, char *digest) { struct h_misc { unsigned long ino; @@ -149,8 +149,13 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, } hmac_misc; memset(&hmac_misc, 0, sizeof(hmac_misc)); - hmac_misc.ino = inode->i_ino; - hmac_misc.generation = inode->i_generation; + /* Don't include the inode or generation number in portable + * signatures + */ + if (type != EVM_XATTR_PORTABLE_DIGSIG) { + hmac_misc.ino = inode->i_ino; + hmac_misc.generation = inode->i_generation; + } /* 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 @@ -163,7 +168,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, 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) + if ((evm_hmac_attrs & EVM_ATTR_FSUUID) && + type != EVM_XATTR_PORTABLE_DIGSIG) crypto_shash_update(desc, &inode->i_sb->s_uuid.b[0], sizeof(inode->i_sb->s_uuid)); crypto_shash_final(desc, digest); @@ -189,6 +195,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, char *xattr_value = NULL; int error; int size; + bool ima_present = false; if (!(inode->i_opflags & IOP_XATTR)) return -EOPNOTSUPP; @@ -199,11 +206,18 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, error = -ENODATA; for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { + bool is_ima = false; + + if (strcmp(*xattrname, XATTR_NAME_IMA) == 0) + is_ima = true; + if ((req_xattr_name && req_xattr_value) && !strcmp(*xattrname, req_xattr_name)) { error = 0; crypto_shash_update(desc, (const u8 *)req_xattr_value, req_xattr_value_len); + if (is_ima) + ima_present = true; continue; } size = vfs_getxattr_alloc(dentry, *xattrname, @@ -218,9 +232,14 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, error = 0; xattr_size = size; crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size); + if (is_ima) + ima_present = true; } - hmac_add_misc(desc, inode, digest); + hmac_add_misc(desc, inode, type, digest); + /* Portable EVM signatures must include an IMA hash */ + if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present) + return -EPERM; out: kfree(xattr_value); kfree(desc); @@ -232,17 +251,45 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, char *digest) { return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, - req_xattr_value_len, EVM_XATTR_HMAC, digest); + req_xattr_value_len, EVM_XATTR_HMAC, digest); } int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, const char *req_xattr_value, size_t req_xattr_value_len, - char *digest) + char type, char *digest) { return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, - req_xattr_value_len, IMA_XATTR_DIGEST, digest); + req_xattr_value_len, type, digest); +} + +static int evm_is_immutable(struct dentry *dentry, struct inode *inode) +{ + const struct evm_ima_xattr_data *xattr_data = NULL; + struct integrity_iint_cache *iint; + int rc = 0; + + iint = integrity_iint_find(inode); + if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG)) + return 1; + + /* Do this the hard way */ + rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, + GFP_NOFS); + if (rc <= 0) { + if (rc == -ENODATA) + return 0; + return rc; + } + if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) + rc = 1; + else + rc = 0; + + kfree(xattr_data); + return rc; } + /* * Calculate the hmac and update security.evm xattr * @@ -255,6 +302,16 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, struct evm_ima_xattr_data xattr_data; int rc = 0; + /* + * Don't permit any transformation of the EVM xattr if the signature + * is of an immutable type + */ + rc = evm_is_immutable(dentry, inode); + if (rc < 0) + return rc; + if (rc) + return -EPERM; + rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, xattr_data.digest); if (rc == 0) { @@ -280,7 +337,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, } crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len); - hmac_add_misc(desc, inode, hmac_val); + hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val); kfree(desc); return 0; } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 9826c02e2db8..a8d502827270 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -31,7 +31,7 @@ int evm_initialized; static char *integrity_status_msg[] = { - "pass", "fail", "no_label", "no_xattrs", "unknown" + "pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown" }; char *evm_hmac = "hmac(sha1)"; char *evm_hash = "sha1"; @@ -76,6 +76,11 @@ static void __init evm_init_config(void) pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs); } +static bool evm_key_loaded(void) +{ + return (bool)(evm_initialized & EVM_KEY_MASK); +} + static int evm_find_protected_xattrs(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); @@ -123,7 +128,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, enum integrity_status evm_status = INTEGRITY_PASS; int rc, xattr_len; - if (iint && iint->evm_status == INTEGRITY_PASS) + if (iint && (iint->evm_status == INTEGRITY_PASS || + iint->evm_status == INTEGRITY_PASS_IMMUTABLE)) return iint->evm_status; /* if status is not PASS, try to check again - against -ENOMEM */ @@ -164,22 +170,26 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, rc = -EINVAL; break; case EVM_IMA_XATTR_DIGSIG: + case EVM_XATTR_PORTABLE_DIGSIG: rc = evm_calc_hash(dentry, xattr_name, xattr_value, - xattr_value_len, calc.digest); + xattr_value_len, xattr_data->type, + calc.digest); if (rc) break; rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, (const char *)xattr_data, xattr_len, calc.digest, sizeof(calc.digest)); if (!rc) { - /* Replace RSA with HMAC if not mounted readonly and - * not immutable - */ - if (!IS_RDONLY(d_backing_inode(dentry)) && - !IS_IMMUTABLE(d_backing_inode(dentry))) + if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) { + if (iint) + iint->flags |= EVM_IMMUTABLE_DIGSIG; + evm_status = INTEGRITY_PASS_IMMUTABLE; + } else if (!IS_RDONLY(d_backing_inode(dentry)) && + !IS_IMMUTABLE(d_backing_inode(dentry))) { evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); + } } break; default: @@ -241,7 +251,7 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry, void *xattr_value, size_t xattr_value_len, struct integrity_iint_cache *iint) { - if (!evm_initialized || !evm_protected_xattr(xattr_name)) + if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) return INTEGRITY_UNKNOWN; if (!iint) { @@ -265,7 +275,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); - if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode) + if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode) return 0; return evm_verify_hmac(dentry, NULL, NULL, 0, NULL); } @@ -280,7 +290,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry) * affect security.evm. An interesting side affect of writing posix xattr * acls is their modifying of the i_mode, which is included in security.evm. * For posix xattr acls only, permit security.evm, even if it currently - * doesn't exist, to be updated. + * doesn't exist, to be updated unless the EVM signature is immutable. */ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) @@ -299,6 +309,7 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, return 0; goto out; } + evm_status = evm_verify_current_integrity(dentry); if (evm_status == INTEGRITY_NOXATTRS) { struct integrity_iint_cache *iint; @@ -345,10 +356,17 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, { const struct evm_ima_xattr_data *xattr_data = xattr_value; + /* Policy permits modification of the protected xattrs even though + * there's no HMAC key loaded + */ + if (evm_initialized & EVM_ALLOW_METADATA_WRITES) + return 0; + if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) { if (!xattr_value_len) return -EINVAL; - if (xattr_data->type != EVM_IMA_XATTR_DIGSIG) + if (xattr_data->type != EVM_IMA_XATTR_DIGSIG && + xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) return -EPERM; } return evm_protect_xattr(dentry, xattr_name, xattr_value, @@ -365,6 +383,12 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, */ int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name) { + /* Policy permits modification of the protected xattrs even though + * there's no HMAC key loaded + */ + if (evm_initialized & EVM_ALLOW_METADATA_WRITES) + return 0; + return evm_protect_xattr(dentry, xattr_name, NULL, 0); } @@ -393,8 +417,8 @@ static void evm_reset_status(struct inode *inode) void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { - if (!evm_initialized || (!evm_protected_xattr(xattr_name) - && !posix_xattr_acl(xattr_name))) + if (!evm_key_loaded() || (!evm_protected_xattr(xattr_name) + && !posix_xattr_acl(xattr_name))) return; evm_reset_status(dentry->d_inode); @@ -414,7 +438,7 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, */ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) { - if (!evm_initialized || !evm_protected_xattr(xattr_name)) + if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) return; evm_reset_status(dentry->d_inode); @@ -425,12 +449,21 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) /** * evm_inode_setattr - prevent updating an invalid EVM extended attribute * @dentry: pointer to the affected dentry + * + * Permit update of file attributes when files have a valid EVM signature, + * except in the case of them having an immutable portable signature. */ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) { unsigned int ia_valid = attr->ia_valid; enum integrity_status evm_status; + /* Policy permits modification of the protected attrs even though + * there's no HMAC key loaded + */ + if (evm_initialized & EVM_ALLOW_METADATA_WRITES) + return 0; + if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))) return 0; evm_status = evm_verify_current_integrity(dentry); @@ -456,7 +489,7 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) */ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) { - if (!evm_initialized) + if (!evm_key_loaded()) return; if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) @@ -473,7 +506,7 @@ int evm_inode_init_security(struct inode *inode, struct evm_ima_xattr_data *xattr_data; int rc; - if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name)) + if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name)) return 0; xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS); diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 319cf16d6603..feba03bbedae 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -40,7 +40,7 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf, if (*ppos != 0) return 0; - sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP)); + sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE)); rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); return rc; @@ -63,7 +63,7 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, { int i, ret; - if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP)) + if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE)) return -EPERM; ret = kstrtoint_from_user(buf, count, 0, &i); @@ -75,16 +75,30 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, if (!i || (i & ~EVM_INIT_MASK) != 0) return -EINVAL; + /* Don't allow a request to freshly enable metadata writes if + * keys are loaded. + */ + if ((i & EVM_ALLOW_METADATA_WRITES) && + ((evm_initialized & EVM_KEY_MASK) != 0) && + !(evm_initialized & EVM_ALLOW_METADATA_WRITES)) + return -EPERM; + if (i & EVM_INIT_HMAC) { ret = evm_init_key(); if (ret != 0) return ret; /* Forbid further writes after the symmetric key is loaded */ - i |= EVM_SETUP; + i |= EVM_SETUP_COMPLETE; } evm_initialized |= i; + /* Don't allow protected metadata modification if a symmetric key + * is loaded + */ + if (evm_initialized & EVM_INIT_HMAC) + evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES); + return count; } diff --git a/security/integrity/iint.c b/security/integrity/iint.c index c84e05866052..fc38ca08dbb5 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -153,14 +153,12 @@ static void init_once(void *foo) struct integrity_iint_cache *iint = foo; memset(iint, 0, sizeof(*iint)); - iint->version = 0; - iint->flags = 0UL; iint->ima_file_status = INTEGRITY_UNKNOWN; iint->ima_mmap_status = INTEGRITY_UNKNOWN; iint->ima_bprm_status = INTEGRITY_UNKNOWN; iint->ima_read_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; - iint->measured_pcrs = 0; + mutex_init(&iint->mutex); } static int __init integrity_iintcache_init(void) diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index c7e8db0ea4c0..08fe405338e1 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -18,6 +18,7 @@ #include <linux/fs.h> #include <linux/xattr.h> #include <linux/evm.h> +#include <linux/iversion.h> #include "ima.h" @@ -174,7 +175,7 @@ err_out: */ int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr) { - int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; + int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH; flags &= ima_policy_flag; @@ -215,7 +216,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, * which do not support i_version, support is limited to an initial * measurement/appraisal/audit. */ - i_version = file_inode(file)->i_version; + i_version = inode_query_iversion(inode); hash.hdr.algo = algo; /* Initialize hash digest to 0's in case of failure */ diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 65fbcf3c32c7..f2803a40ff82 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -46,14 +46,15 @@ bool is_ima_appraise_enabled(void) /* * ima_must_appraise - set appraise flag * - * Return 1 to appraise + * Return 1 to appraise or hash */ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func) { if (!ima_appraise) return 0; - return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL); + return ima_match_policy(inode, func, mask, IMA_APPRAISE | IMA_HASH, + NULL); } static int ima_fix_xattr(struct dentry *dentry, @@ -223,13 +224,16 @@ int ima_appraise_measurement(enum ima_hooks func, if (opened & FILE_CREATED) iint->flags |= IMA_NEW_FILE; if ((iint->flags & IMA_NEW_FILE) && - !(iint->flags & IMA_DIGSIG_REQUIRED)) + (!(iint->flags & IMA_DIGSIG_REQUIRED) || + (inode->i_size == 0))) status = INTEGRITY_PASS; goto out; } status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); - if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { + if ((status != INTEGRITY_PASS) && + (status != INTEGRITY_PASS_IMMUTABLE) && + (status != INTEGRITY_UNKNOWN)) { if ((status == INTEGRITY_NOLABEL) || (status == INTEGRITY_NOXATTRS)) cause = "missing-HMAC"; @@ -248,6 +252,7 @@ int ima_appraise_measurement(enum ima_hooks func, status = INTEGRITY_FAIL; break; } + clear_bit(IMA_DIGSIG, &iint->atomic_flags); if (xattr_len - sizeof(xattr_value->type) - hash_start >= iint->ima_hash->length) /* xattr length may be longer. md5 hash in previous @@ -266,7 +271,7 @@ int ima_appraise_measurement(enum ima_hooks func, status = INTEGRITY_PASS; break; case EVM_IMA_XATTR_DIGSIG: - iint->flags |= IMA_DIGSIG; + set_bit(IMA_DIGSIG, &iint->atomic_flags); rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, (const char *)xattr_value, rc, iint->ima_hash->digest, @@ -317,17 +322,20 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) int rc = 0; /* do not collect and update hash for digital signatures */ - if (iint->flags & IMA_DIGSIG) + if (test_bit(IMA_DIGSIG, &iint->atomic_flags)) return; - if (iint->ima_file_status != INTEGRITY_PASS) + if ((iint->ima_file_status != INTEGRITY_PASS) && + !(iint->flags & IMA_HASH)) return; rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo); if (rc < 0) return; + inode_lock(file_inode(file)); ima_fix_xattr(dentry, iint); + inode_unlock(file_inode(file)); } /** @@ -343,23 +351,21 @@ void ima_inode_post_setattr(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); struct integrity_iint_cache *iint; - int must_appraise; + int action; if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode) || !(inode->i_opflags & IOP_XATTR)) return; - must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); + action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); + if (!action) + __vfs_removexattr(dentry, XATTR_NAME_IMA); iint = integrity_iint_find(inode); if (iint) { - iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | - IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | - IMA_ACTION_RULE_FLAGS); - if (must_appraise) - iint->flags |= IMA_APPRAISE; + set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags); + if (!action) + clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); } - if (!must_appraise) - __vfs_removexattr(dentry, XATTR_NAME_IMA); } /* @@ -388,12 +394,12 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig) iint = integrity_iint_find(inode); if (!iint) return; - - iint->flags &= ~IMA_DONE_MASK; iint->measured_pcrs = 0; + set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags); if (digsig) - iint->flags |= IMA_DIGSIG; - return; + set_bit(IMA_DIGSIG, &iint->atomic_flags); + else + clear_bit(IMA_DIGSIG, &iint->atomic_flags); } int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 9057b163c378..205bc69361ea 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -632,7 +632,7 @@ static void __init ima_pcrread(int idx, u8 *pcr) if (!ima_used_chip) return; - if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0) + if (tpm_pcr_read(NULL, idx, pcr) != 0) pr_err("Error Communicating to TPM chip\n"); } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 2967d497a665..29b72cd2502e 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -110,7 +110,7 @@ int __init ima_init(void) int rc; ima_used_chip = 0; - rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i); + rc = tpm_pcr_read(NULL, 0, pcr_i); if (rc == 0) ima_used_chip = 1; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 770654694efc..061425dd6400 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/xattr.h> #include <linux/ima.h> +#include <linux/iversion.h> #include "ima.h" @@ -84,10 +85,10 @@ static void ima_rdwr_violation_check(struct file *file, struct integrity_iint_cache *iint, int must_measure, char **pathbuf, - const char **pathname) + const char **pathname, + char *filename) { struct inode *inode = file_inode(file); - char filename[NAME_MAX]; fmode_t mode = file->f_mode; bool send_tomtou = false, send_writers = false; @@ -96,10 +97,13 @@ static void ima_rdwr_violation_check(struct file *file, if (!iint) iint = integrity_iint_find(inode); /* IMA_MEASURE is set from reader side */ - if (iint && (iint->flags & IMA_MEASURE)) + if (iint && test_bit(IMA_MUST_MEASURE, + &iint->atomic_flags)) send_tomtou = true; } } else { + if (must_measure) + set_bit(IMA_MUST_MEASURE, &iint->atomic_flags); if ((atomic_read(&inode->i_writecount) > 0) && must_measure) send_writers = true; } @@ -121,21 +125,25 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, struct inode *inode, struct file *file) { fmode_t mode = file->f_mode; + bool update; if (!(mode & FMODE_WRITE)) return; - inode_lock(inode); + mutex_lock(&iint->mutex); if (atomic_read(&inode->i_writecount) == 1) { - if ((iint->version != inode->i_version) || + update = test_and_clear_bit(IMA_UPDATE_XATTR, + &iint->atomic_flags); + if (!IS_I_VERSION(inode) || + inode_cmp_iversion(inode, iint->version) || (iint->flags & IMA_NEW_FILE)) { iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); iint->measured_pcrs = 0; - if (iint->flags & IMA_APPRAISE) + if (update) ima_update_xattr(iint, file); } } - inode_unlock(inode); + mutex_unlock(&iint->mutex); } /** @@ -168,7 +176,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size, char *pathbuf = NULL; char filename[NAME_MAX]; const char *pathname = NULL; - int rc = -ENOMEM, action, must_appraise; + int rc = 0, action, must_appraise = 0; int pcr = CONFIG_IMA_MEASURE_PCR_IDX; struct evm_ima_xattr_data *xattr_value = NULL; int xattr_len = 0; @@ -199,17 +207,31 @@ static int process_measurement(struct file *file, char *buf, loff_t size, if (action) { iint = integrity_inode_get(inode); if (!iint) - goto out; + rc = -ENOMEM; } - if (violation_check) { + if (!rc && violation_check) ima_rdwr_violation_check(file, iint, action & IMA_MEASURE, - &pathbuf, &pathname); - if (!action) { - rc = 0; - goto out_free; - } - } + &pathbuf, &pathname, filename); + + inode_unlock(inode); + + if (rc) + goto out; + if (!action) + goto out; + + mutex_lock(&iint->mutex); + + if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags)) + /* reset appraisal flags if ima_inode_post_setattr was called */ + iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | + IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | + IMA_ACTION_FLAGS); + + if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags)) + /* reset all flags if ima_inode_setxattr was called */ + iint->flags &= ~IMA_DONE_MASK; /* Determine if already appraised/measured based on bitmask * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED, @@ -223,11 +245,23 @@ static int process_measurement(struct file *file, char *buf, loff_t size, if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr))) action ^= IMA_MEASURE; + /* HASH sets the digital signature and update flags, nothing else */ + if ((action & IMA_HASH) && + !(test_bit(IMA_DIGSIG, &iint->atomic_flags))) { + xattr_len = ima_read_xattr(file_dentry(file), &xattr_value); + if ((xattr_value && xattr_len > 2) && + (xattr_value->type == EVM_IMA_XATTR_DIGSIG)) + set_bit(IMA_DIGSIG, &iint->atomic_flags); + iint->flags |= IMA_HASHED; + action ^= IMA_HASH; + set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); + } + /* Nothing to do, just return existing appraised status */ if (!action) { if (must_appraise) rc = ima_get_cache_status(iint, func); - goto out_digsig; + goto out_locked; } template_desc = ima_template_desc_current(); @@ -240,7 +274,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size, rc = ima_collect_measurement(iint, file, buf, size, hash_algo); if (rc != 0 && rc != -EBADF && rc != -EINVAL) - goto out_digsig; + goto out_locked; if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */ pathname = ima_d_path(&file->f_path, &pathbuf, filename); @@ -248,26 +282,32 @@ static int process_measurement(struct file *file, char *buf, loff_t size, if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname, xattr_value, xattr_len, pcr); - if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) + if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { + inode_lock(inode); rc = ima_appraise_measurement(func, iint, file, pathname, xattr_value, xattr_len, opened); + inode_unlock(inode); + } if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO)) rc = 0; -out_digsig: - if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) && +out_locked: + if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) && !(iint->flags & IMA_NEW_FILE)) rc = -EACCES; + mutex_unlock(&iint->mutex); kfree(xattr_value); -out_free: +out: if (pathbuf) __putname(pathbuf); -out: - inode_unlock(inode); - if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) - return -EACCES; + if (must_appraise) { + if (rc && (ima_appraise & IMA_APPRAISE_ENFORCE)) + return -EACCES; + if (file->f_mode & FMODE_WRITE) + set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); + } return 0; } @@ -366,8 +406,10 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id) if (!file && read_id == READING_MODULE) { if (!sig_enforce && (ima_appraise & IMA_APPRAISE_MODULES) && - (ima_appraise & IMA_APPRAISE_ENFORCE)) + (ima_appraise & IMA_APPRAISE_ENFORCE)) { + pr_err("impossible to appraise a module without a file descriptor. sig_enforce kernel parameter might help\n"); return -EACCES; /* INTEGRITY_UNKNOWN */ + } return 0; /* We rely on module signature checking */ } return 0; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index ee4613fa5840..915f5572c6ff 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -40,6 +40,8 @@ #define APPRAISE 0x0004 /* same as IMA_APPRAISE */ #define DONT_APPRAISE 0x0008 #define AUDIT 0x0040 +#define HASH 0x0100 +#define DONT_HASH 0x0200 #define INVALID_PCR(a) (((a) < 0) || \ (a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8)) @@ -380,8 +382,10 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, action |= entry->flags & IMA_ACTION_FLAGS; action |= entry->action & IMA_DO_MASK; - if (entry->action & IMA_APPRAISE) + if (entry->action & IMA_APPRAISE) { action |= get_subaction(entry, func); + action ^= IMA_HASH; + } if (entry->action & IMA_DO_MASK) actmask &= ~(entry->action | entry->action << 1); @@ -521,7 +525,7 @@ enum { Opt_err = -1, Opt_measure = 1, Opt_dont_measure, Opt_appraise, Opt_dont_appraise, - Opt_audit, + Opt_audit, Opt_hash, Opt_dont_hash, Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, Opt_func, Opt_mask, Opt_fsmagic, @@ -538,6 +542,8 @@ static match_table_t policy_tokens = { {Opt_appraise, "appraise"}, {Opt_dont_appraise, "dont_appraise"}, {Opt_audit, "audit"}, + {Opt_hash, "hash"}, + {Opt_dont_hash, "dont_hash"}, {Opt_obj_user, "obj_user=%s"}, {Opt_obj_role, "obj_role=%s"}, {Opt_obj_type, "obj_type=%s"}, @@ -671,6 +677,22 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->action = AUDIT; break; + case Opt_hash: + ima_log_string(ab, "action", "hash"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = HASH; + break; + case Opt_dont_hash: + ima_log_string(ab, "action", "dont_hash"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = DONT_HASH; + break; case Opt_func: ima_log_string(ab, "func", args[0].from); @@ -743,7 +765,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) case Opt_fsuuid: ima_log_string(ab, "fsuuid", args[0].from); - if (uuid_is_null(&entry->fsuuid)) { + if (!uuid_is_null(&entry->fsuuid)) { result = -EINVAL; break; } @@ -1040,6 +1062,10 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, pt(Opt_dont_appraise)); if (entry->action & AUDIT) seq_puts(m, pt(Opt_audit)); + if (entry->action & HASH) + seq_puts(m, pt(Opt_hash)); + if (entry->action & DONT_HASH) + seq_puts(m, pt(Opt_dont_hash)); seq_puts(m, " "); diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index a02a86d51102..418f35e38015 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -145,7 +145,7 @@ static int ima_pcr_extend(const u8 *hash, int pcr) if (!ima_used_chip) return result; - result = tpm_pcr_extend(TPM_ANY_NUM, pcr, hash); + result = tpm_pcr_extend(NULL, pcr, hash); if (result != 0) pr_err("Error Communicating to TPM chip, result: %d\n", result); return result; diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 7412d0291ab9..30db39b23804 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -377,8 +377,7 @@ int ima_restore_measurement_list(loff_t size, void *buf) break; if (hdr[HDR_TEMPLATE_NAME].len >= MAX_TEMPLATE_NAME_LEN) { - pr_err("attempting to restore a template name \ - that is too long\n"); + pr_err("attempting to restore a template name that is too long\n"); ret = -EINVAL; break; } @@ -389,8 +388,8 @@ int ima_restore_measurement_list(loff_t size, void *buf) template_name[hdr[HDR_TEMPLATE_NAME].len] = 0; if (strcmp(template_name, "ima") == 0) { - pr_err("attempting to restore an unsupported \ - template \"%s\" failed\n", template_name); + pr_err("attempting to restore an unsupported template \"%s\" failed\n", + template_name); ret = -EINVAL; break; } @@ -410,8 +409,8 @@ int ima_restore_measurement_list(loff_t size, void *buf) &(template_desc->fields), &(template_desc->num_fields)); if (ret < 0) { - pr_err("attempting to restore the template fmt \"%s\" \ - failed\n", template_desc->fmt); + pr_err("attempting to restore the template fmt \"%s\" failed\n", + template_desc->fmt); ret = -EINVAL; break; } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index e1bf040fb110..50a8e3365df7 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -25,39 +25,50 @@ #define IMA_COLLECTED 0x00000020 #define IMA_AUDIT 0x00000040 #define IMA_AUDITED 0x00000080 +#define IMA_HASH 0x00000100 +#define IMA_HASHED 0x00000200 /* iint cache flags */ #define IMA_ACTION_FLAGS 0xff000000 #define IMA_ACTION_RULE_FLAGS 0x06000000 -#define IMA_DIGSIG 0x01000000 -#define IMA_DIGSIG_REQUIRED 0x02000000 -#define IMA_PERMIT_DIRECTIO 0x04000000 -#define IMA_NEW_FILE 0x08000000 +#define IMA_DIGSIG_REQUIRED 0x01000000 +#define IMA_PERMIT_DIRECTIO 0x02000000 +#define IMA_NEW_FILE 0x04000000 +#define EVM_IMMUTABLE_DIGSIG 0x08000000 #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ - IMA_APPRAISE_SUBMASK) + IMA_HASH | IMA_APPRAISE_SUBMASK) #define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \ - IMA_COLLECTED | IMA_APPRAISED_SUBMASK) + IMA_HASHED | IMA_COLLECTED | \ + IMA_APPRAISED_SUBMASK) /* iint subaction appraise cache flags */ -#define IMA_FILE_APPRAISE 0x00000100 -#define IMA_FILE_APPRAISED 0x00000200 -#define IMA_MMAP_APPRAISE 0x00000400 -#define IMA_MMAP_APPRAISED 0x00000800 -#define IMA_BPRM_APPRAISE 0x00001000 -#define IMA_BPRM_APPRAISED 0x00002000 -#define IMA_READ_APPRAISE 0x00004000 -#define IMA_READ_APPRAISED 0x00008000 +#define IMA_FILE_APPRAISE 0x00001000 +#define IMA_FILE_APPRAISED 0x00002000 +#define IMA_MMAP_APPRAISE 0x00004000 +#define IMA_MMAP_APPRAISED 0x00008000 +#define IMA_BPRM_APPRAISE 0x00010000 +#define IMA_BPRM_APPRAISED 0x00020000 +#define IMA_READ_APPRAISE 0x00040000 +#define IMA_READ_APPRAISED 0x00080000 #define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \ IMA_BPRM_APPRAISE | IMA_READ_APPRAISE) #define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \ IMA_BPRM_APPRAISED | IMA_READ_APPRAISED) +/* iint cache atomic_flags */ +#define IMA_CHANGE_XATTR 0 +#define IMA_UPDATE_XATTR 1 +#define IMA_CHANGE_ATTR 2 +#define IMA_DIGSIG 3 +#define IMA_MUST_MEASURE 4 + enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, EVM_XATTR_HMAC, EVM_IMA_XATTR_DIGSIG, IMA_XATTR_DIGEST_NG, + EVM_XATTR_PORTABLE_DIGSIG, IMA_XATTR_LAST }; @@ -100,10 +111,12 @@ struct signature_v2_hdr { /* integrity data associated with an inode */ struct integrity_iint_cache { struct rb_node rb_node; /* rooted in integrity_iint_tree */ + struct mutex mutex; /* protects: version, flags, digest */ struct inode *inode; /* back pointer to inode in question */ u64 version; /* track inode changes */ unsigned long flags; unsigned long measured_pcrs; + unsigned long atomic_flags; enum integrity_status ima_file_status:4; enum integrity_status ima_mmap_status:4; enum integrity_status ima_bprm_status:4; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d0bccebbd3b5..41bcf57e96f2 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -713,7 +713,6 @@ descend_to_keyring: * doesn't contain any keyring pointers. */ shortcut = assoc_array_ptr_to_shortcut(ptr); - smp_read_barrier_depends(); if ((shortcut->index_key[0] & ASSOC_ARRAY_FAN_MASK) != 0) goto not_this_keyring; @@ -723,8 +722,6 @@ descend_to_keyring: } node = assoc_array_ptr_to_node(ptr); - smp_read_barrier_depends(); - ptr = node->slots[0]; if (!assoc_array_ptr_is_meta(ptr)) goto begin_node; @@ -736,7 +733,6 @@ descend_to_node: kdebug("descend"); if (assoc_array_ptr_is_shortcut(ptr)) { shortcut = assoc_array_ptr_to_shortcut(ptr); - smp_read_barrier_depends(); ptr = READ_ONCE(shortcut->next_node); BUG_ON(!assoc_array_ptr_is_node(ptr)); } @@ -744,7 +740,6 @@ descend_to_node: begin_node: kdebug("begin_node"); - smp_read_barrier_depends(); slot = 0; ascend_to_node: /* Go through the slots in a node */ @@ -792,14 +787,12 @@ ascend_to_node: if (ptr && assoc_array_ptr_is_shortcut(ptr)) { shortcut = assoc_array_ptr_to_shortcut(ptr); - smp_read_barrier_depends(); ptr = READ_ONCE(shortcut->back_pointer); slot = shortcut->parent_slot; } if (!ptr) goto not_this_keyring; node = assoc_array_ptr_to_node(ptr); - smp_read_barrier_depends(); slot++; /* If we've ascended to the root (zero backpointer), we must have just diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 98aa89ff7bfd..423776682025 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -355,13 +355,12 @@ out: * For key specific tpm requests, we will generate and send our * own TPM command packets using the drivers send function. */ -static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd, - size_t buflen) +static int trusted_tpm_send(unsigned char *cmd, size_t buflen) { int rc; dump_tpm_buf(cmd); - rc = tpm_send(chip_num, cmd, buflen); + rc = tpm_send(NULL, cmd, buflen); dump_tpm_buf(cmd); if (rc > 0) /* Can't return positive return codes values to keyctl */ @@ -382,10 +381,10 @@ static int pcrlock(const int pcrnum) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE); + ret = tpm_get_random(NULL, hash, SHA1_DIGEST_SIZE); if (ret != SHA1_DIGEST_SIZE) return ret; - return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0; + return tpm_pcr_extend(NULL, pcrnum, hash) ? -EINVAL : 0; } /* @@ -398,7 +397,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, unsigned char ononce[TPM_NONCE_SIZE]; int ret; - ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE); + ret = tpm_get_random(NULL, ononce, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) return ret; @@ -410,7 +409,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, store32(tb, handle); storebytes(tb, ononce, TPM_NONCE_SIZE); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); if (ret < 0) return ret; @@ -434,7 +433,7 @@ static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce) store16(tb, TPM_TAG_RQU_COMMAND); store32(tb, TPM_OIAP_SIZE); store32(tb, TPM_ORD_OIAP); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); if (ret < 0) return ret; @@ -493,7 +492,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, if (ret < 0) goto out; - ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE); + ret = tpm_get_random(NULL, td->nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) goto out; ordinal = htonl(TPM_ORD_SEAL); @@ -542,7 +541,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, store8(tb, cont); storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); if (ret < 0) goto out; @@ -603,7 +602,7 @@ static int tpm_unseal(struct tpm_buf *tb, ordinal = htonl(TPM_ORD_UNSEAL); keyhndl = htonl(SRKHANDLE); - ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE); + ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); if (ret != TPM_NONCE_SIZE) { pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); return ret; @@ -635,7 +634,7 @@ static int tpm_unseal(struct tpm_buf *tb, store8(tb, cont); storebytes(tb, authdata2, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); if (ret < 0) { pr_info("trusted_key: authhmac failed (%d)\n", ret); return ret; @@ -748,7 +747,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay, int i; int tpm2; - tpm2 = tpm_is_tpm2(TPM_ANY_NUM); + tpm2 = tpm_is_tpm2(NULL); if (tpm2 < 0) return tpm2; @@ -917,7 +916,7 @@ static struct trusted_key_options *trusted_options_alloc(void) struct trusted_key_options *options; int tpm2; - tpm2 = tpm_is_tpm2(TPM_ANY_NUM); + tpm2 = tpm_is_tpm2(NULL); if (tpm2 < 0) return NULL; @@ -967,7 +966,7 @@ static int trusted_instantiate(struct key *key, size_t key_len; int tpm2; - tpm2 = tpm_is_tpm2(TPM_ANY_NUM); + tpm2 = tpm_is_tpm2(NULL); if (tpm2 < 0) return tpm2; @@ -1008,7 +1007,7 @@ static int trusted_instantiate(struct key *key, switch (key_cmd) { case Opt_load: if (tpm2) - ret = tpm_unseal_trusted(TPM_ANY_NUM, payload, options); + ret = tpm_unseal_trusted(NULL, payload, options); else ret = key_unseal(payload, options); dump_payload(payload); @@ -1018,13 +1017,13 @@ static int trusted_instantiate(struct key *key, break; case Opt_new: key_len = payload->key_len; - ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len); + ret = tpm_get_random(NULL, payload->key, key_len); if (ret != key_len) { pr_info("trusted_key: key_create failed (%d)\n", ret); goto out; } if (tpm2) - ret = tpm_seal_trusted(TPM_ANY_NUM, payload, options); + ret = tpm_seal_trusted(NULL, payload, options); else ret = key_seal(payload, options); if (ret < 0) diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index 75686d53df07..e77a5e307955 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -19,8 +19,7 @@ * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index aaba6677ee2e..2c297b995b16 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -22,8 +22,7 @@ * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 33cfe5d3d6cb..8900ea5cbabf 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -867,6 +867,9 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) int index; int rc; + if (!ss_initialized) + return 0; + read_lock(&policy_rwlock); rc = -EINVAL; @@ -1413,27 +1416,25 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, if (!scontext_len) return -EINVAL; + /* Copy the string to allow changes and ensure a NUL terminator */ + scontext2 = kmemdup_nul(scontext, scontext_len, gfp_flags); + if (!scontext2) + return -ENOMEM; + if (!ss_initialized) { int i; for (i = 1; i < SECINITSID_NUM; i++) { - if (!strcmp(initial_sid_to_string[i], scontext)) { + if (!strcmp(initial_sid_to_string[i], scontext2)) { *sid = i; - return 0; + goto out; } } *sid = SECINITSID_KERNEL; - return 0; + goto out; } *sid = SECSID_NULL; - /* Copy the string so that we can modify the copy as we parse it. */ - scontext2 = kmalloc(scontext_len + 1, gfp_flags); - if (!scontext2) - return -ENOMEM; - memcpy(scontext2, scontext, scontext_len); - scontext2[scontext_len] = 0; - if (force) { /* Save another copy for storing in uninterpreted form */ rc = -ENOMEM; diff --git a/security/smack/smack.h b/security/smack/smack.h index 6a71fc7831ab..f7db791fb566 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -321,6 +321,7 @@ 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 *); bool smack_privileged(int cap); +bool smack_privileged_cred(int cap, const struct cred *cred); void smk_destroy_label_list(struct list_head *list); /* diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 1a3004189447..9a4c0ad46518 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -623,26 +623,24 @@ struct smack_known *smack_from_secid(const u32 secid) LIST_HEAD(smack_onlycap_list); DEFINE_MUTEX(smack_onlycap_lock); -/* +/** + * smack_privileged_cred - are all privilege requirements met by cred + * @cap: The requested capability + * @cred: the credential to use + * * Is the task privileged and allowed to be privileged * by the onlycap rule. * * Returns true if the task is allowed to be privileged, false if it's not. */ -bool smack_privileged(int cap) +bool smack_privileged_cred(int cap, const struct cred *cred) { - struct smack_known *skp = smk_of_current(); + struct task_smack *tsp = cred->security; + struct smack_known *skp = tsp->smk_task; struct smack_known_list_elem *sklep; int rc; - /* - * All kernel tasks are privileged - */ - if (unlikely(current->flags & PF_KTHREAD)) - return true; - - rc = cap_capable(current_cred(), &init_user_ns, cap, - SECURITY_CAP_AUDIT); + rc = cap_capable(cred, &init_user_ns, cap, SECURITY_CAP_AUDIT); if (rc) return false; @@ -662,3 +660,23 @@ bool smack_privileged(int cap) return false; } + +/** + * smack_privileged - are all privilege requirements met + * @cap: The requested capability + * + * Is the task privileged and allowed to be privileged + * by the onlycap rule. + * + * Returns true if the task is allowed to be privileged, false if it's not. + */ +bool smack_privileged(int cap) +{ + /* + * All kernel tasks are privileged + */ + if (unlikely(current->flags & PF_KTHREAD)) + return true; + + return smack_privileged_cred(cap, current_cred()); +} diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 14cc7940b36d..03fdecba93bb 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2866,12 +2866,16 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, #endif #ifdef SMACK_IPV6_SECMARK_LABELING struct smack_known *rsp; - struct socket_smack *ssp = sock->sk->sk_security; + struct socket_smack *ssp; #endif if (sock->sk == NULL) return 0; +#ifdef SMACK_IPV6_SECMARK_LABELING + ssp = sock->sk->sk_security; +#endif + switch (sock->sk->sk_family) { case PF_INET: if (addrlen < sizeof(struct sockaddr_in)) @@ -4365,6 +4369,10 @@ static int smack_key_permission(key_ref_t key_ref, */ if (tkp == NULL) return -EACCES; + + if (smack_privileged_cred(CAP_MAC_OVERRIDE, cred)) + return 0; + #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY); ad.a.u.key_struct.key = keyp->serial; diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 0f73fe30e37a..558e3076d38c 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -458,7 +458,7 @@ void tomoyo_read_log(struct tomoyo_io_buffer *head) * * Returns POLLIN | POLLRDNORM when ready to read an audit log. */ -unsigned int tomoyo_poll_log(struct file *file, poll_table *wait) +__poll_t tomoyo_poll_log(struct file *file, poll_table *wait) { if (tomoyo_log_count) return POLLIN | POLLRDNORM; diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 25eed4b0b0e8..70c73bf66c88 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -2120,7 +2120,7 @@ static struct tomoyo_domain_info *tomoyo_find_domain_by_qid * * Waits for access requests which violated policy in enforcing mode. */ -static unsigned int tomoyo_poll_query(struct file *file, poll_table *wait) +static __poll_t tomoyo_poll_query(struct file *file, poll_table *wait) { if (!list_empty(&tomoyo_query_list)) return POLLIN | POLLRDNORM; @@ -2453,7 +2453,7 @@ int tomoyo_open_control(const u8 type, struct file *file) * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, * POLLOUT | POLLWRNORM otherwise. */ -unsigned int tomoyo_poll_control(struct file *file, poll_table *wait) +__poll_t tomoyo_poll_control(struct file *file, poll_table *wait) { struct tomoyo_io_buffer *head = file->private_data; if (head->poll) diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 7adccdd8e36d..539bcdd30bb8 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -789,7 +789,7 @@ struct tomoyo_acl_param { struct tomoyo_io_buffer { void (*read) (struct tomoyo_io_buffer *); int (*write) (struct tomoyo_io_buffer *); - unsigned int (*poll) (struct file *file, poll_table *wait); + __poll_t (*poll) (struct file *file, poll_table *wait); /* Exclusive lock for this structure. */ struct mutex io_sem; char __user *read_user_buf; @@ -981,8 +981,8 @@ int tomoyo_path_number_perm(const u8 operation, const struct path *path, unsigned long number); int tomoyo_path_perm(const u8 operation, const struct path *path, const char *target); -unsigned int tomoyo_poll_control(struct file *file, poll_table *wait); -unsigned int tomoyo_poll_log(struct file *file, poll_table *wait); +__poll_t tomoyo_poll_control(struct file *file, poll_table *wait); +__poll_t tomoyo_poll_log(struct file *file, poll_table *wait); int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, int addr_len); int tomoyo_socket_connect_permission(struct socket *sock, diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 49393c2a3f8b..fb9bf99deb35 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -157,7 +157,7 @@ static int tomoyo_release(struct inode *inode, struct file *file) * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, * POLLOUT | POLLWRNORM otherwise. */ -static unsigned int tomoyo_poll(struct file *file, poll_table *wait) +static __poll_t tomoyo_poll(struct file *file, poll_table *wait) { return tomoyo_poll_control(file, wait); } |