18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2010 IBM Corporation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Mimi Zohar <zohar@us.ibm.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * File: evm_secfs.c 98c2ecf20Sopenharmony_ci * - Used to signal when key is on keyring 108c2ecf20Sopenharmony_ci * - Get the key and enable EVM 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/audit.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include "evm.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic struct dentry *evm_dir; 208c2ecf20Sopenharmony_cistatic struct dentry *evm_init_tpm; 218c2ecf20Sopenharmony_cistatic struct dentry *evm_symlink; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#ifdef CONFIG_EVM_ADD_XATTRS 248c2ecf20Sopenharmony_cistatic struct dentry *evm_xattrs; 258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(xattr_list_mutex); 268c2ecf20Sopenharmony_cistatic int evm_xattrs_locked; 278c2ecf20Sopenharmony_ci#endif 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/** 308c2ecf20Sopenharmony_ci * evm_read_key - read() for <securityfs>/evm 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * @filp: file pointer, not actually used 338c2ecf20Sopenharmony_ci * @buf: where to put the result 348c2ecf20Sopenharmony_ci * @count: maximum to send along 358c2ecf20Sopenharmony_ci * @ppos: where to start 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Returns number of bytes read or error code, as appropriate 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic ssize_t evm_read_key(struct file *filp, char __user *buf, 408c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci char temp[80]; 438c2ecf20Sopenharmony_ci ssize_t rc; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (*ppos != 0) 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE)); 498c2ecf20Sopenharmony_ci rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return rc; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * evm_write_key - write() for <securityfs>/evm 568c2ecf20Sopenharmony_ci * @file: file pointer, not actually used 578c2ecf20Sopenharmony_ci * @buf: where to get the data from 588c2ecf20Sopenharmony_ci * @count: bytes sent 598c2ecf20Sopenharmony_ci * @ppos: where to start 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * Used to signal that key is on the kernel key ring. 628c2ecf20Sopenharmony_ci * - get the integrity hmac key from the kernel key ring 638c2ecf20Sopenharmony_ci * - create list of hmac protected extended attributes 648c2ecf20Sopenharmony_ci * Returns number of bytes written or error code, as appropriate 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic ssize_t evm_write_key(struct file *file, const char __user *buf, 678c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci unsigned int i; 708c2ecf20Sopenharmony_ci int ret; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE)) 738c2ecf20Sopenharmony_ci return -EPERM; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci ret = kstrtouint_from_user(buf, count, 0, &i); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (ret) 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* Reject invalid values */ 818c2ecf20Sopenharmony_ci if (!i || (i & ~EVM_INIT_MASK) != 0) 828c2ecf20Sopenharmony_ci return -EINVAL; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* 858c2ecf20Sopenharmony_ci * Don't allow a request to enable metadata writes if 868c2ecf20Sopenharmony_ci * an HMAC key is loaded. 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci if ((i & EVM_ALLOW_METADATA_WRITES) && 898c2ecf20Sopenharmony_ci (evm_initialized & EVM_INIT_HMAC) != 0) 908c2ecf20Sopenharmony_ci return -EPERM; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (i & EVM_INIT_HMAC) { 938c2ecf20Sopenharmony_ci ret = evm_init_key(); 948c2ecf20Sopenharmony_ci if (ret != 0) 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci /* Forbid further writes after the symmetric key is loaded */ 978c2ecf20Sopenharmony_ci i |= EVM_SETUP_COMPLETE; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci evm_initialized |= i; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Don't allow protected metadata modification if a symmetric key 1038c2ecf20Sopenharmony_ci * is loaded 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci if (evm_initialized & EVM_INIT_HMAC) 1068c2ecf20Sopenharmony_ci evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return count; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct file_operations evm_key_ops = { 1128c2ecf20Sopenharmony_ci .read = evm_read_key, 1138c2ecf20Sopenharmony_ci .write = evm_write_key, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#ifdef CONFIG_EVM_ADD_XATTRS 1178c2ecf20Sopenharmony_ci/** 1188c2ecf20Sopenharmony_ci * evm_read_xattrs - read() for <securityfs>/evm_xattrs 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * @filp: file pointer, not actually used 1218c2ecf20Sopenharmony_ci * @buf: where to put the result 1228c2ecf20Sopenharmony_ci * @count: maximum to send along 1238c2ecf20Sopenharmony_ci * @ppos: where to start 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * Returns number of bytes read or error code, as appropriate 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_cistatic ssize_t evm_read_xattrs(struct file *filp, char __user *buf, 1288c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci char *temp; 1318c2ecf20Sopenharmony_ci int offset = 0; 1328c2ecf20Sopenharmony_ci ssize_t rc, size = 0; 1338c2ecf20Sopenharmony_ci struct xattr_list *xattr; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (*ppos != 0) 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci rc = mutex_lock_interruptible(&xattr_list_mutex); 1398c2ecf20Sopenharmony_ci if (rc) 1408c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci list_for_each_entry(xattr, &evm_config_xattrnames, list) 1438c2ecf20Sopenharmony_ci size += strlen(xattr->name) + 1; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci temp = kmalloc(size + 1, GFP_KERNEL); 1468c2ecf20Sopenharmony_ci if (!temp) { 1478c2ecf20Sopenharmony_ci mutex_unlock(&xattr_list_mutex); 1488c2ecf20Sopenharmony_ci return -ENOMEM; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci list_for_each_entry(xattr, &evm_config_xattrnames, list) { 1528c2ecf20Sopenharmony_ci sprintf(temp + offset, "%s\n", xattr->name); 1538c2ecf20Sopenharmony_ci offset += strlen(xattr->name) + 1; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci mutex_unlock(&xattr_list_mutex); 1578c2ecf20Sopenharmony_ci rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci kfree(temp); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return rc; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/** 1658c2ecf20Sopenharmony_ci * evm_write_xattrs - write() for <securityfs>/evm_xattrs 1668c2ecf20Sopenharmony_ci * @file: file pointer, not actually used 1678c2ecf20Sopenharmony_ci * @buf: where to get the data from 1688c2ecf20Sopenharmony_ci * @count: bytes sent 1698c2ecf20Sopenharmony_ci * @ppos: where to start 1708c2ecf20Sopenharmony_ci * 1718c2ecf20Sopenharmony_ci * Returns number of bytes written or error code, as appropriate 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistatic ssize_t evm_write_xattrs(struct file *file, const char __user *buf, 1748c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int len, err; 1778c2ecf20Sopenharmony_ci struct xattr_list *xattr, *tmp; 1788c2ecf20Sopenharmony_ci struct audit_buffer *ab; 1798c2ecf20Sopenharmony_ci struct iattr newattrs; 1808c2ecf20Sopenharmony_ci struct inode *inode; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked) 1838c2ecf20Sopenharmony_ci return -EPERM; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (*ppos != 0) 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (count > XATTR_NAME_MAX) 1898c2ecf20Sopenharmony_ci return -E2BIG; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ab = audit_log_start(audit_context(), GFP_KERNEL, 1928c2ecf20Sopenharmony_ci AUDIT_INTEGRITY_EVM_XATTR); 1938c2ecf20Sopenharmony_ci if (!ab) 1948c2ecf20Sopenharmony_ci return -ENOMEM; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL); 1978c2ecf20Sopenharmony_ci if (!xattr) { 1988c2ecf20Sopenharmony_ci err = -ENOMEM; 1998c2ecf20Sopenharmony_ci goto out; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci xattr->name = memdup_user_nul(buf, count); 2038c2ecf20Sopenharmony_ci if (IS_ERR(xattr->name)) { 2048c2ecf20Sopenharmony_ci err = PTR_ERR(xattr->name); 2058c2ecf20Sopenharmony_ci xattr->name = NULL; 2068c2ecf20Sopenharmony_ci goto out; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Remove any trailing newline */ 2108c2ecf20Sopenharmony_ci len = strlen(xattr->name); 2118c2ecf20Sopenharmony_ci if (len && xattr->name[len-1] == '\n') 2128c2ecf20Sopenharmony_ci xattr->name[len-1] = '\0'; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci audit_log_format(ab, "xattr="); 2158c2ecf20Sopenharmony_ci audit_log_untrustedstring(ab, xattr->name); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (strcmp(xattr->name, ".") == 0) { 2188c2ecf20Sopenharmony_ci evm_xattrs_locked = 1; 2198c2ecf20Sopenharmony_ci newattrs.ia_mode = S_IFREG | 0440; 2208c2ecf20Sopenharmony_ci newattrs.ia_valid = ATTR_MODE; 2218c2ecf20Sopenharmony_ci inode = evm_xattrs->d_inode; 2228c2ecf20Sopenharmony_ci inode_lock(inode); 2238c2ecf20Sopenharmony_ci err = simple_setattr(evm_xattrs, &newattrs); 2248c2ecf20Sopenharmony_ci inode_unlock(inode); 2258c2ecf20Sopenharmony_ci if (!err) 2268c2ecf20Sopenharmony_ci err = count; 2278c2ecf20Sopenharmony_ci goto out; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (strncmp(xattr->name, XATTR_SECURITY_PREFIX, 2318c2ecf20Sopenharmony_ci XATTR_SECURITY_PREFIX_LEN) != 0) { 2328c2ecf20Sopenharmony_ci err = -EINVAL; 2338c2ecf20Sopenharmony_ci goto out; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* 2378c2ecf20Sopenharmony_ci * xattr_list_mutex guards against races in evm_read_xattrs(). 2388c2ecf20Sopenharmony_ci * Entries are only added to the evm_config_xattrnames list 2398c2ecf20Sopenharmony_ci * and never deleted. Therefore, the list is traversed 2408c2ecf20Sopenharmony_ci * using list_for_each_entry_lockless() without holding 2418c2ecf20Sopenharmony_ci * the mutex in evm_calc_hmac_or_hash(), evm_find_protected_xattrs() 2428c2ecf20Sopenharmony_ci * and evm_protected_xattr(). 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci mutex_lock(&xattr_list_mutex); 2458c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &evm_config_xattrnames, list) { 2468c2ecf20Sopenharmony_ci if (strcmp(xattr->name, tmp->name) == 0) { 2478c2ecf20Sopenharmony_ci err = -EEXIST; 2488c2ecf20Sopenharmony_ci mutex_unlock(&xattr_list_mutex); 2498c2ecf20Sopenharmony_ci goto out; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci list_add_tail_rcu(&xattr->list, &evm_config_xattrnames); 2538c2ecf20Sopenharmony_ci mutex_unlock(&xattr_list_mutex); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci audit_log_format(ab, " res=0"); 2568c2ecf20Sopenharmony_ci audit_log_end(ab); 2578c2ecf20Sopenharmony_ci return count; 2588c2ecf20Sopenharmony_ciout: 2598c2ecf20Sopenharmony_ci audit_log_format(ab, " res=%d", err); 2608c2ecf20Sopenharmony_ci audit_log_end(ab); 2618c2ecf20Sopenharmony_ci if (xattr) { 2628c2ecf20Sopenharmony_ci kfree(xattr->name); 2638c2ecf20Sopenharmony_ci kfree(xattr); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci return err; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct file_operations evm_xattr_ops = { 2698c2ecf20Sopenharmony_ci .read = evm_read_xattrs, 2708c2ecf20Sopenharmony_ci .write = evm_write_xattrs, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int evm_init_xattrs(void) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL, 2768c2ecf20Sopenharmony_ci &evm_xattr_ops); 2778c2ecf20Sopenharmony_ci if (!evm_xattrs || IS_ERR(evm_xattrs)) 2788c2ecf20Sopenharmony_ci return -EFAULT; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci#else 2838c2ecf20Sopenharmony_cistatic int evm_init_xattrs(void) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci#endif 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciint __init evm_init_secfs(void) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci int error = 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci evm_dir = securityfs_create_dir("evm", integrity_dir); 2948c2ecf20Sopenharmony_ci if (!evm_dir || IS_ERR(evm_dir)) 2958c2ecf20Sopenharmony_ci return -EFAULT; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci evm_init_tpm = securityfs_create_file("evm", 0660, 2988c2ecf20Sopenharmony_ci evm_dir, NULL, &evm_key_ops); 2998c2ecf20Sopenharmony_ci if (!evm_init_tpm || IS_ERR(evm_init_tpm)) { 3008c2ecf20Sopenharmony_ci error = -EFAULT; 3018c2ecf20Sopenharmony_ci goto out; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci evm_symlink = securityfs_create_symlink("evm", NULL, 3058c2ecf20Sopenharmony_ci "integrity/evm/evm", NULL); 3068c2ecf20Sopenharmony_ci if (!evm_symlink || IS_ERR(evm_symlink)) { 3078c2ecf20Sopenharmony_ci error = -EFAULT; 3088c2ecf20Sopenharmony_ci goto out; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (evm_init_xattrs() != 0) { 3128c2ecf20Sopenharmony_ci error = -EFAULT; 3138c2ecf20Sopenharmony_ci goto out; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ciout: 3188c2ecf20Sopenharmony_ci securityfs_remove(evm_symlink); 3198c2ecf20Sopenharmony_ci securityfs_remove(evm_init_tpm); 3208c2ecf20Sopenharmony_ci securityfs_remove(evm_dir); 3218c2ecf20Sopenharmony_ci return error; 3228c2ecf20Sopenharmony_ci} 323