/* * This function determines whether or not it is safe to ignore verification * errors, based on the ability of EVM to calculate HMACs. If the HMAC key * is not loaded, and it cannot be loaded in the future due to the * EVM_SETUP_COMPLETE initialization flag, allowing an operation despite the * attrs/xattrs being found invalid will not make them valid.
*/ staticbool evm_hmac_disabled(void)
{ if (evm_initialized & EVM_INIT_HMAC) returnfalse;
if (!(evm_initialized & EVM_SETUP_COMPLETE)) returnfalse;
returntrue;
}
staticint evm_find_protected_xattrs(struct dentry *dentry)
{ struct inode *inode = d_backing_inode(dentry); struct xattr_list *xattr; int error; int count = 0;
if (!(inode->i_opflags & IOP_XATTR)) return -EOPNOTSUPP;
if (inode->i_sb->s_iflags & SB_I_EVM_HMAC_UNSUPPORTED) {
pr_info_once("%s not supported\n", inode->i_sb->s_type->name); return 1;
} return 0;
}
/* * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr * * Compute the HMAC on the dentry's protected set of extended attributes * and compare it against the stored security.evm xattr. * * For performance: * - use the previously retrieved xattr value and length to calculate the * HMAC.) * - cache the verification result in the iint, when available. * * Returns integrity status
*/ staticenum integrity_status evm_verify_hmac(struct dentry *dentry, constchar *xattr_name, char *xattr_value,
size_t xattr_value_len)
{ struct evm_ima_xattr_data *xattr_data = NULL; struct signature_v2_hdr *hdr; enum integrity_status evm_status = INTEGRITY_PASS; struct evm_digest digest; struct inode *inode = d_backing_inode(dentry); struct evm_iint_cache *iint = evm_iint_inode(inode); int rc, xattr_len, evm_immutable = 0;
if ((strlen(xattr->name) == namelen)
&& (strncmp(req_xattr_name, xattr->name, namelen) == 0)) {
found = 1; break;
} if (strncmp(req_xattr_name,
xattr->name + XATTR_SECURITY_PREFIX_LEN,
strlen(req_xattr_name)) == 0) {
found = 1; break;
}
}
return found;
}
int evm_protected_xattr(constchar *req_xattr_name)
{ return evm_protected_xattr_common(req_xattr_name, false);
}
int evm_protected_xattr_if_enabled(constchar *req_xattr_name)
{ return evm_protected_xattr_common(req_xattr_name, true);
}
/** * evm_read_protected_xattrs - read EVM protected xattr names, lengths, values * @dentry: dentry of the read xattrs * @buffer: buffer xattr names, lengths or values are copied to * @buffer_size: size of buffer * @type: n: names, l: lengths, v: values * @canonical_fmt: data format (true: little endian, false: native format) * * Read protected xattr names (separated by |), lengths (u32) or values for a * given dentry and return the total size of copied data. If buffer is NULL, * just return the total size. * * Returns the total size on success, a negative value on error.
*/ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, int buffer_size, char type, bool canonical_fmt)
{ struct xattr_list *xattr; int rc, size, total_size = 0;
/** * evm_verifyxattr - verify the integrity of the requested xattr * @dentry: object of the verify xattr * @xattr_name: requested xattr * @xattr_value: requested xattr value * @xattr_value_len: requested xattr value length * * Calculate the HMAC for the given dentry and verify it against the stored * security.evm xattr. For performance, use the xattr value and length * previously retrieved to calculate the HMAC. * * Returns the xattr integrity status. * * This function requires the caller to lock the inode's i_mutex before it * is executed.
*/ enum integrity_status evm_verifyxattr(struct dentry *dentry, constchar *xattr_name, void *xattr_value, size_t xattr_value_len)
{ if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) return INTEGRITY_UNKNOWN;
/* * evm_verify_current_integrity - verify the dentry's metadata integrity * @dentry: pointer to the affected dentry * * Verify and return the dentry's metadata integrity. The exceptions are * before EVM is initialized or in 'fix' mode.
*/ staticenum integrity_status evm_verify_current_integrity(struct dentry *dentry)
{ struct inode *inode = d_backing_inode(dentry);
/* * evm_xattr_change - check if passed xattr value differs from current value * @idmap: idmap of the mount * @dentry: pointer to the affected dentry * @xattr_name: requested xattr * @xattr_value: requested xattr value * @xattr_value_len: requested xattr value length * * Check if passed xattr value differs from current value. * * Returns 1 if passed xattr value differs from current value, 0 otherwise.
*/ staticint evm_xattr_change(struct mnt_idmap *idmap, struct dentry *dentry, constchar *xattr_name, constvoid *xattr_value, size_t xattr_value_len)
{ char *xattr_data = NULL; int rc = 0;
/* * evm_protect_xattr - protect the EVM extended attribute * * Prevent security.evm from being modified or removed without the * necessary permissions or when the existing value is invalid. * * The posix xattr acls are 'system' prefixed, which normally would not * 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 unless the EVM signature is immutable.
*/ staticint evm_protect_xattr(struct mnt_idmap *idmap, struct dentry *dentry, constchar *xattr_name, constvoid *xattr_value, size_t xattr_value_len)
{ enum integrity_status evm_status;
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) { if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (is_unsupported_hmac_fs(dentry)) return -EPERM;
} elseif (!evm_protected_xattr(xattr_name)) { if (!posix_xattr_acl(xattr_name)) return 0; if (is_unsupported_hmac_fs(dentry)) return 0;
/* exception for pseudo filesystems */ if (dentry->d_sb->s_magic == TMPFS_MAGIC
|| dentry->d_sb->s_magic == SYSFS_MAGIC) return 0;
integrity_audit_msg(AUDIT_INTEGRITY_METADATA,
dentry->d_inode, dentry->d_name.name, "update_metadata",
integrity_status_msg[evm_status],
-EPERM, 0);
}
out: /* Exception if the HMAC is not going to be calculated. */ if (evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL ||
evm_status == INTEGRITY_UNKNOWN)) return 0;
/* * Writing other xattrs is safe for portable signatures, as portable * signatures are immutable and can never be updated.
*/ if (evm_status == INTEGRITY_FAIL_IMMUTABLE) return 0;
/** * evm_inode_setxattr - protect the EVM extended attribute * @idmap: idmap of the mount * @dentry: pointer to the affected dentry * @xattr_name: pointer to the affected extended attribute name * @xattr_value: pointer to the new extended attribute value * @xattr_value_len: pointer to the new extended attribute value length * @flags: flags to pass into filesystem operations * * Before allowing the 'security.evm' protected xattr to be updated, * verify the existing value is valid. As only the kernel should have * access to the EVM encrypted key needed to calculate the HMAC, prevent * userspace from writing HMAC value. Writing 'security.evm' requires * requires CAP_SYS_ADMIN privileges.
*/ staticint evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, constchar *xattr_name, constvoid *xattr_value,
size_t xattr_value_len, int flags)
{ conststruct 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 &&
xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG) return -EPERM;
} return evm_protect_xattr(idmap, dentry, xattr_name, xattr_value,
xattr_value_len);
}
/** * evm_inode_removexattr - protect the EVM extended attribute * @idmap: idmap of the mount * @dentry: pointer to the affected dentry * @xattr_name: pointer to the affected extended attribute name * * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that * the current value is valid.
*/ staticint evm_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry, constchar *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;
/** * evm_inode_set_acl - protect the EVM extended attribute from posix acls * @idmap: idmap of the idmapped mount * @dentry: pointer to the affected dentry * @acl_name: name of the posix acl * @kacl: pointer to the posix acls * * Prevent modifying posix acls causing the EVM HMAC to be re-calculated * and 'security.evm' xattr updated, unless the existing 'security.evm' is * valid. * * Return: zero on success, -EPERM on failure.
*/ staticint evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, constchar *acl_name, struct posix_acl *kacl)
{ enum integrity_status evm_status;
/* Policy permits modification of the protected xattrs even though * there's no HMAC key loaded
*/ if (evm_initialized & EVM_ALLOW_METADATA_WRITES) return 0;
/* Exception if the HMAC is not going to be calculated. */ if (evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL ||
evm_status == INTEGRITY_UNKNOWN)) return 0;
/* * Writing other xattrs is safe for portable signatures, as portable * signatures are immutable and can never be updated.
*/ if (evm_status == INTEGRITY_FAIL_IMMUTABLE) return 0;
/** * evm_inode_remove_acl - Protect the EVM extended attribute from posix acls * @idmap: idmap of the mount * @dentry: pointer to the affected dentry * @acl_name: name of the posix acl * * Prevent removing posix acls causing the EVM HMAC to be re-calculated * and 'security.evm' xattr updated, unless the existing 'security.evm' is * valid. * * Return: zero on success, -EPERM on failure.
*/ staticint evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, constchar *acl_name)
{ return evm_inode_set_acl(idmap, dentry, acl_name, NULL);
}
iint = evm_iint_inode(inode); if (iint)
iint->evm_status = INTEGRITY_UNKNOWN;
}
/** * evm_metadata_changed: Detect changes to the metadata * @inode: a file's inode * @metadata_inode: metadata inode * * On a stacked filesystem detect whether the metadata has changed. If this is * the case reset the evm_status associated with the inode that represents the * file.
*/ bool evm_metadata_changed(struct inode *inode, struct inode *metadata_inode)
{ struct evm_iint_cache *iint = evm_iint_inode(inode); bool ret = false;
if (iint) {
ret = (!IS_I_VERSION(metadata_inode) ||
integrity_inode_attrs_changed(&iint->metadata_inode,
metadata_inode)); if (ret)
iint->evm_status = INTEGRITY_UNKNOWN;
}
return ret;
}
/** * evm_revalidate_status - report whether EVM status re-validation is necessary * @xattr_name: pointer to the affected extended attribute name * * Report whether callers of evm_verifyxattr() should re-validate the * EVM status. * * Return true if re-validation is necessary, false otherwise.
*/ bool evm_revalidate_status(constchar *xattr_name)
{ if (!evm_key_loaded()) returnfalse;
/* evm_inode_post_setattr() passes NULL */ if (!xattr_name) returntrue;
if (!evm_protected_xattr(xattr_name) && !posix_xattr_acl(xattr_name) &&
strcmp(xattr_name, XATTR_NAME_EVM)) returnfalse;
returntrue;
}
/** * evm_inode_post_setxattr - update 'security.evm' to reflect the changes * @dentry: pointer to the affected dentry * @xattr_name: pointer to the affected extended attribute name * @xattr_value: pointer to the new extended attribute value * @xattr_value_len: pointer to the new extended attribute value length * @flags: flags to pass into filesystem operations * * Update the HMAC stored in 'security.evm' to reflect the change. * * No need to take the i_mutex lock here, as this function is called from * __vfs_setxattr_noperm(). The caller of which has taken the inode's * i_mutex lock.
*/ staticvoid evm_inode_post_setxattr(struct dentry *dentry, constchar *xattr_name, constvoid *xattr_value,
size_t xattr_value_len, int flags)
{ if (!evm_revalidate_status(xattr_name)) return;
/** * evm_inode_post_set_acl - Update the EVM extended attribute from posix acls * @dentry: pointer to the affected dentry * @acl_name: name of the posix acl * @kacl: pointer to the posix acls * * Update the 'security.evm' xattr with the EVM HMAC re-calculated after setting * posix acls.
*/ staticvoid evm_inode_post_set_acl(struct dentry *dentry, constchar *acl_name, struct posix_acl *kacl)
{ return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0);
}
/** * evm_inode_post_removexattr - update 'security.evm' after removing the xattr * @dentry: pointer to the affected dentry * @xattr_name: pointer to the affected extended attribute name * * Update the HMAC stored in 'security.evm' to reflect removal of the xattr. * * No need to take the i_mutex lock here, as this function is called from * vfs_removexattr() which takes the i_mutex.
*/ staticvoid evm_inode_post_removexattr(struct dentry *dentry, constchar *xattr_name)
{ if (!evm_revalidate_status(xattr_name)) return;
/** * evm_inode_post_remove_acl - Update the EVM extended attribute from posix acls * @idmap: idmap of the mount * @dentry: pointer to the affected dentry * @acl_name: name of the posix acl * * Update the 'security.evm' xattr with the EVM HMAC re-calculated after * removing posix acls.
*/ staticinlinevoid evm_inode_post_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry, constchar *acl_name)
{
evm_inode_post_removexattr(dentry, acl_name);
}
/** * evm_inode_setattr - prevent updating an invalid EVM extended attribute * @idmap: idmap of the mount * @dentry: pointer to the affected dentry * @attr: iattr structure containing the new file attributes * * Permit update of file attributes when files have a valid EVM signature, * except in the case of them having an immutable portable signature.
*/ staticint evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr)
{ unsignedint 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 (is_unsupported_hmac_fs(dentry)) return 0;
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))) return 0;
evm_status = evm_verify_current_integrity(dentry); /* * Writing attrs is safe for portable signatures, as portable signatures * are immutable and can never be updated.
*/ if ((evm_status == INTEGRITY_PASS) ||
(evm_status == INTEGRITY_NOXATTRS) ||
(evm_status == INTEGRITY_FAIL_IMMUTABLE) ||
(evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL ||
evm_status == INTEGRITY_UNKNOWN))) return 0;
if (evm_status == INTEGRITY_PASS_IMMUTABLE &&
!evm_attr_change(idmap, dentry, attr)) return 0;
/** * evm_inode_post_setattr - update 'security.evm' after modifying metadata * @idmap: idmap of the idmapped mount * @dentry: pointer to the affected dentry * @ia_valid: for the UID and GID status * * For now, update the HMAC stored in 'security.evm' to reflect UID/GID * changes. * * This function is called from notify_change(), which expects the caller * to lock the inode's i_mutex.
*/ staticvoid evm_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry, int ia_valid)
{ if (!evm_revalidate_status(NULL)) return;
if (strcmp(name, XATTR_NAME_EVM) != 0) return -EOPNOTSUPP;
/* first need to know the sig type */
rc = vfs_getxattr_alloc(&nop_mnt_idmap, src, XATTR_NAME_EVM,
(char **)&xattr_data, 0, GFP_NOFS); if (rc <= 0) return -EPERM;
if (rc < offsetof(struct evm_ima_xattr_data, type) + sizeof(xattr_data->type)) return -EPERM;
switch (xattr_data->type) { case EVM_XATTR_PORTABLE_DIGSIG:
rc = 0; /* allow copy-up */ break; case EVM_XATTR_HMAC: case EVM_IMA_XATTR_DIGSIG: default:
rc = -ECANCELED; /* discard */
}
kfree(xattr_data); return rc;
}
/* * evm_inode_init_security - initializes security.evm HMAC value
*/ int evm_inode_init_security(struct inode *inode, struct inode *dir, conststruct qstr *qstr, struct xattr *xattrs, int *xattr_count)
{ struct evm_xattr *xattr_data; struct xattr *xattr, *evm_xattr; bool evm_protected_xattrs = false; int rc;
if (!(evm_initialized & EVM_INIT_HMAC) || !xattrs) return 0;
/* * security_inode_init_security() makes sure that the xattrs array is * contiguous, there is enough space for security.evm, and that there is * a terminator at the end of the array.
*/ for (xattr = xattrs; xattr->name; xattr++) { if (evm_protected_xattr(xattr->name))
evm_protected_xattrs = true;
}
/* EVM xattr not needed. */ if (!evm_protected_xattrs) return 0;
evm_xattr = lsm_get_xattr_slot(xattrs, xattr_count); /* * Array terminator (xattr name = NULL) must be the first non-filled * xattr slot.
*/
WARN_ONCE(evm_xattr != xattr, "%s: xattrs terminator is not the first non-filled slot\n",
__func__);
xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS); if (!xattr_data) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.