166dbc325SMimi Zohar /* 266dbc325SMimi Zohar * Copyright (C) 2010 IBM Corporation 366dbc325SMimi Zohar * 466dbc325SMimi Zohar * Authors: 566dbc325SMimi Zohar * Mimi Zohar <zohar@us.ibm.com> 666dbc325SMimi Zohar * 766dbc325SMimi Zohar * This program is free software; you can redistribute it and/or modify 866dbc325SMimi Zohar * it under the terms of the GNU General Public License as published by 966dbc325SMimi Zohar * the Free Software Foundation, version 2 of the License. 1066dbc325SMimi Zohar * 1166dbc325SMimi Zohar * File: evm_secfs.c 1266dbc325SMimi Zohar * - Used to signal when key is on keyring 1366dbc325SMimi Zohar * - Get the key and enable EVM 1466dbc325SMimi Zohar */ 1566dbc325SMimi Zohar 1620ee451fSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1720ee451fSJoe Perches 18fa516b66SMatthew Garrett #include <linux/audit.h> 1966dbc325SMimi Zohar #include <linux/uaccess.h> 2066dbc325SMimi Zohar #include <linux/module.h> 21fa516b66SMatthew Garrett #include <linux/mutex.h> 2266dbc325SMimi Zohar #include "evm.h" 2366dbc325SMimi Zohar 240c343af8SMatthew Garrett static struct dentry *evm_dir; 2566dbc325SMimi Zohar static struct dentry *evm_init_tpm; 260c343af8SMatthew Garrett static struct dentry *evm_symlink; 2766dbc325SMimi Zohar 28fa516b66SMatthew Garrett #ifdef CONFIG_EVM_ADD_XATTRS 29fa516b66SMatthew Garrett static struct dentry *evm_xattrs; 30fa516b66SMatthew Garrett static DEFINE_MUTEX(xattr_list_mutex); 31fa516b66SMatthew Garrett static int evm_xattrs_locked; 32fa516b66SMatthew Garrett #endif 33fa516b66SMatthew Garrett 3466dbc325SMimi Zohar /** 3566dbc325SMimi Zohar * evm_read_key - read() for <securityfs>/evm 3666dbc325SMimi Zohar * 3766dbc325SMimi Zohar * @filp: file pointer, not actually used 3866dbc325SMimi Zohar * @buf: where to put the result 3966dbc325SMimi Zohar * @count: maximum to send along 4066dbc325SMimi Zohar * @ppos: where to start 4166dbc325SMimi Zohar * 4266dbc325SMimi Zohar * Returns number of bytes read or error code, as appropriate 4366dbc325SMimi Zohar */ 4466dbc325SMimi Zohar static ssize_t evm_read_key(struct file *filp, char __user *buf, 4566dbc325SMimi Zohar size_t count, loff_t *ppos) 4666dbc325SMimi Zohar { 4766dbc325SMimi Zohar char temp[80]; 4866dbc325SMimi Zohar ssize_t rc; 4966dbc325SMimi Zohar 5066dbc325SMimi Zohar if (*ppos != 0) 5166dbc325SMimi Zohar return 0; 5266dbc325SMimi Zohar 53ae1ba167SMatthew Garrett sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE)); 5466dbc325SMimi Zohar rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); 5566dbc325SMimi Zohar 5666dbc325SMimi Zohar return rc; 5766dbc325SMimi Zohar } 5866dbc325SMimi Zohar 5966dbc325SMimi Zohar /** 6066dbc325SMimi Zohar * evm_write_key - write() for <securityfs>/evm 6166dbc325SMimi Zohar * @file: file pointer, not actually used 6266dbc325SMimi Zohar * @buf: where to get the data from 6366dbc325SMimi Zohar * @count: bytes sent 6466dbc325SMimi Zohar * @ppos: where to start 6566dbc325SMimi Zohar * 6666dbc325SMimi Zohar * Used to signal that key is on the kernel key ring. 6766dbc325SMimi Zohar * - get the integrity hmac key from the kernel key ring 6866dbc325SMimi Zohar * - create list of hmac protected extended attributes 6966dbc325SMimi Zohar * Returns number of bytes written or error code, as appropriate 7066dbc325SMimi Zohar */ 7166dbc325SMimi Zohar static ssize_t evm_write_key(struct file *file, const char __user *buf, 7266dbc325SMimi Zohar size_t count, loff_t *ppos) 7366dbc325SMimi Zohar { 74f00d7975SMatthew Garrett int i, ret; 7566dbc325SMimi Zohar 76ae1ba167SMatthew Garrett if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE)) 7766dbc325SMimi Zohar return -EPERM; 7866dbc325SMimi Zohar 79f00d7975SMatthew Garrett ret = kstrtoint_from_user(buf, count, 0, &i); 80f00d7975SMatthew Garrett 81f00d7975SMatthew Garrett if (ret) 82f00d7975SMatthew Garrett return ret; 83f00d7975SMatthew Garrett 84f00d7975SMatthew Garrett /* Reject invalid values */ 85f00d7975SMatthew Garrett if (!i || (i & ~EVM_INIT_MASK) != 0) 8666dbc325SMimi Zohar return -EINVAL; 8766dbc325SMimi Zohar 88ae1ba167SMatthew Garrett /* Don't allow a request to freshly enable metadata writes if 89ae1ba167SMatthew Garrett * keys are loaded. 90ae1ba167SMatthew Garrett */ 91ae1ba167SMatthew Garrett if ((i & EVM_ALLOW_METADATA_WRITES) && 92ae1ba167SMatthew Garrett ((evm_initialized & EVM_KEY_MASK) != 0) && 93ae1ba167SMatthew Garrett !(evm_initialized & EVM_ALLOW_METADATA_WRITES)) 94ae1ba167SMatthew Garrett return -EPERM; 95ae1ba167SMatthew Garrett 96f00d7975SMatthew Garrett if (i & EVM_INIT_HMAC) { 97f00d7975SMatthew Garrett ret = evm_init_key(); 98f00d7975SMatthew Garrett if (ret != 0) 99f00d7975SMatthew Garrett return ret; 100f00d7975SMatthew Garrett /* Forbid further writes after the symmetric key is loaded */ 101ae1ba167SMatthew Garrett i |= EVM_SETUP_COMPLETE; 102f00d7975SMatthew Garrett } 10366dbc325SMimi Zohar 104f00d7975SMatthew Garrett evm_initialized |= i; 10576266763SDmitry Kasatkin 106ae1ba167SMatthew Garrett /* Don't allow protected metadata modification if a symmetric key 107ae1ba167SMatthew Garrett * is loaded 108ae1ba167SMatthew Garrett */ 109ae1ba167SMatthew Garrett if (evm_initialized & EVM_INIT_HMAC) 110ae1ba167SMatthew Garrett evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES); 111ae1ba167SMatthew Garrett 11266dbc325SMimi Zohar return count; 11366dbc325SMimi Zohar } 11466dbc325SMimi Zohar 11566dbc325SMimi Zohar static const struct file_operations evm_key_ops = { 11666dbc325SMimi Zohar .read = evm_read_key, 11766dbc325SMimi Zohar .write = evm_write_key, 11866dbc325SMimi Zohar }; 11966dbc325SMimi Zohar 120fa516b66SMatthew Garrett #ifdef CONFIG_EVM_ADD_XATTRS 121fa516b66SMatthew Garrett /** 122fa516b66SMatthew Garrett * evm_read_xattrs - read() for <securityfs>/evm_xattrs 123fa516b66SMatthew Garrett * 124fa516b66SMatthew Garrett * @filp: file pointer, not actually used 125fa516b66SMatthew Garrett * @buf: where to put the result 126fa516b66SMatthew Garrett * @count: maximum to send along 127fa516b66SMatthew Garrett * @ppos: where to start 128fa516b66SMatthew Garrett * 129fa516b66SMatthew Garrett * Returns number of bytes read or error code, as appropriate 130fa516b66SMatthew Garrett */ 131fa516b66SMatthew Garrett static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, 132fa516b66SMatthew Garrett size_t count, loff_t *ppos) 133fa516b66SMatthew Garrett { 134fa516b66SMatthew Garrett char *temp; 135fa516b66SMatthew Garrett int offset = 0; 136fa516b66SMatthew Garrett ssize_t rc, size = 0; 137fa516b66SMatthew Garrett struct xattr_list *xattr; 138fa516b66SMatthew Garrett 139fa516b66SMatthew Garrett if (*ppos != 0) 140fa516b66SMatthew Garrett return 0; 141fa516b66SMatthew Garrett 142fa516b66SMatthew Garrett rc = mutex_lock_interruptible(&xattr_list_mutex); 143fa516b66SMatthew Garrett if (rc) 144fa516b66SMatthew Garrett return -ERESTARTSYS; 145fa516b66SMatthew Garrett 146fa516b66SMatthew Garrett list_for_each_entry(xattr, &evm_config_xattrnames, list) 147fa516b66SMatthew Garrett size += strlen(xattr->name) + 1; 148fa516b66SMatthew Garrett 149fa516b66SMatthew Garrett temp = kmalloc(size + 1, GFP_KERNEL); 150b5c90a75SDan Carpenter if (!temp) { 151b5c90a75SDan Carpenter mutex_unlock(&xattr_list_mutex); 152fa516b66SMatthew Garrett return -ENOMEM; 153b5c90a75SDan Carpenter } 154fa516b66SMatthew Garrett 155fa516b66SMatthew Garrett list_for_each_entry(xattr, &evm_config_xattrnames, list) { 156fa516b66SMatthew Garrett sprintf(temp + offset, "%s\n", xattr->name); 157fa516b66SMatthew Garrett offset += strlen(xattr->name) + 1; 158fa516b66SMatthew Garrett } 159fa516b66SMatthew Garrett 160fa516b66SMatthew Garrett mutex_unlock(&xattr_list_mutex); 161fa516b66SMatthew Garrett rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); 162fa516b66SMatthew Garrett 163825b8650SColin Ian King kfree(temp); 164825b8650SColin Ian King 165fa516b66SMatthew Garrett return rc; 166fa516b66SMatthew Garrett } 167fa516b66SMatthew Garrett 168fa516b66SMatthew Garrett /** 169fa516b66SMatthew Garrett * evm_write_xattrs - write() for <securityfs>/evm_xattrs 170fa516b66SMatthew Garrett * @file: file pointer, not actually used 171fa516b66SMatthew Garrett * @buf: where to get the data from 172fa516b66SMatthew Garrett * @count: bytes sent 173fa516b66SMatthew Garrett * @ppos: where to start 174fa516b66SMatthew Garrett * 175fa516b66SMatthew Garrett * Returns number of bytes written or error code, as appropriate 176fa516b66SMatthew Garrett */ 177fa516b66SMatthew Garrett static ssize_t evm_write_xattrs(struct file *file, const char __user *buf, 178fa516b66SMatthew Garrett size_t count, loff_t *ppos) 179fa516b66SMatthew Garrett { 180fa516b66SMatthew Garrett int len, err; 181fa516b66SMatthew Garrett struct xattr_list *xattr, *tmp; 182fa516b66SMatthew Garrett struct audit_buffer *ab; 183fa516b66SMatthew Garrett struct iattr newattrs; 184fa516b66SMatthew Garrett struct inode *inode; 185fa516b66SMatthew Garrett 186fa516b66SMatthew Garrett if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked) 187fa516b66SMatthew Garrett return -EPERM; 188fa516b66SMatthew Garrett 189fa516b66SMatthew Garrett if (*ppos != 0) 190fa516b66SMatthew Garrett return -EINVAL; 191fa516b66SMatthew Garrett 192fa516b66SMatthew Garrett if (count > XATTR_NAME_MAX) 193fa516b66SMatthew Garrett return -E2BIG; 194fa516b66SMatthew Garrett 195fa516b66SMatthew Garrett ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR); 1963dd0f18cSWei Yongjun if (!ab) 1973dd0f18cSWei Yongjun return -ENOMEM; 198fa516b66SMatthew Garrett 199fa516b66SMatthew Garrett xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL); 200fa516b66SMatthew Garrett if (!xattr) { 201fa516b66SMatthew Garrett err = -ENOMEM; 202fa516b66SMatthew Garrett goto out; 203fa516b66SMatthew Garrett } 204fa516b66SMatthew Garrett 205fa516b66SMatthew Garrett xattr->name = memdup_user_nul(buf, count); 206fa516b66SMatthew Garrett if (IS_ERR(xattr->name)) { 207fa516b66SMatthew Garrett err = PTR_ERR(xattr->name); 208fa516b66SMatthew Garrett xattr->name = NULL; 209fa516b66SMatthew Garrett goto out; 210fa516b66SMatthew Garrett } 211fa516b66SMatthew Garrett 212fa516b66SMatthew Garrett /* Remove any trailing newline */ 213fa516b66SMatthew Garrett len = strlen(xattr->name); 214a41d80acSDan Carpenter if (len && xattr->name[len-1] == '\n') 215fa516b66SMatthew Garrett xattr->name[len-1] = '\0'; 216fa516b66SMatthew Garrett 217fa516b66SMatthew Garrett if (strcmp(xattr->name, ".") == 0) { 218fa516b66SMatthew Garrett evm_xattrs_locked = 1; 219fa516b66SMatthew Garrett newattrs.ia_mode = S_IFREG | 0440; 220fa516b66SMatthew Garrett newattrs.ia_valid = ATTR_MODE; 221fa516b66SMatthew Garrett inode = evm_xattrs->d_inode; 222fa516b66SMatthew Garrett inode_lock(inode); 223fa516b66SMatthew Garrett err = simple_setattr(evm_xattrs, &newattrs); 224fa516b66SMatthew Garrett inode_unlock(inode); 225fa516b66SMatthew Garrett audit_log_format(ab, "locked"); 226fa516b66SMatthew Garrett if (!err) 227fa516b66SMatthew Garrett err = count; 228fa516b66SMatthew Garrett goto out; 229fa516b66SMatthew Garrett } 230fa516b66SMatthew Garrett 231fa516b66SMatthew Garrett audit_log_format(ab, "xattr="); 232fa516b66SMatthew Garrett audit_log_untrustedstring(ab, xattr->name); 233fa516b66SMatthew Garrett 234fa516b66SMatthew Garrett if (strncmp(xattr->name, XATTR_SECURITY_PREFIX, 235fa516b66SMatthew Garrett XATTR_SECURITY_PREFIX_LEN) != 0) { 236fa516b66SMatthew Garrett err = -EINVAL; 237fa516b66SMatthew Garrett goto out; 238fa516b66SMatthew Garrett } 239fa516b66SMatthew Garrett 240fa516b66SMatthew Garrett /* Guard against races in evm_read_xattrs */ 241fa516b66SMatthew Garrett mutex_lock(&xattr_list_mutex); 242fa516b66SMatthew Garrett list_for_each_entry(tmp, &evm_config_xattrnames, list) { 243fa516b66SMatthew Garrett if (strcmp(xattr->name, tmp->name) == 0) { 244fa516b66SMatthew Garrett err = -EEXIST; 245fa516b66SMatthew Garrett mutex_unlock(&xattr_list_mutex); 246fa516b66SMatthew Garrett goto out; 247fa516b66SMatthew Garrett } 248fa516b66SMatthew Garrett } 249fa516b66SMatthew Garrett list_add_tail_rcu(&xattr->list, &evm_config_xattrnames); 250fa516b66SMatthew Garrett mutex_unlock(&xattr_list_mutex); 251fa516b66SMatthew Garrett 252fa516b66SMatthew Garrett audit_log_format(ab, " res=0"); 253fa516b66SMatthew Garrett audit_log_end(ab); 254fa516b66SMatthew Garrett return count; 255fa516b66SMatthew Garrett out: 256fa516b66SMatthew Garrett audit_log_format(ab, " res=%d", err); 257fa516b66SMatthew Garrett audit_log_end(ab); 25872acd64dSColin Ian King if (xattr) { 259fa516b66SMatthew Garrett kfree(xattr->name); 260fa516b66SMatthew Garrett kfree(xattr); 26172acd64dSColin Ian King } 262fa516b66SMatthew Garrett return err; 263fa516b66SMatthew Garrett } 264fa516b66SMatthew Garrett 265fa516b66SMatthew Garrett static const struct file_operations evm_xattr_ops = { 266fa516b66SMatthew Garrett .read = evm_read_xattrs, 267fa516b66SMatthew Garrett .write = evm_write_xattrs, 268fa516b66SMatthew Garrett }; 269fa516b66SMatthew Garrett 270fa516b66SMatthew Garrett static int evm_init_xattrs(void) 271fa516b66SMatthew Garrett { 272fa516b66SMatthew Garrett evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL, 273fa516b66SMatthew Garrett &evm_xattr_ops); 274fa516b66SMatthew Garrett if (!evm_xattrs || IS_ERR(evm_xattrs)) 275fa516b66SMatthew Garrett return -EFAULT; 276fa516b66SMatthew Garrett 277fa516b66SMatthew Garrett return 0; 278fa516b66SMatthew Garrett } 279fa516b66SMatthew Garrett #else 280fa516b66SMatthew Garrett static int evm_init_xattrs(void) 281fa516b66SMatthew Garrett { 282fa516b66SMatthew Garrett return 0; 283fa516b66SMatthew Garrett } 284fa516b66SMatthew Garrett #endif 285fa516b66SMatthew Garrett 28666dbc325SMimi Zohar int __init evm_init_secfs(void) 28766dbc325SMimi Zohar { 28866dbc325SMimi Zohar int error = 0; 28966dbc325SMimi Zohar 2900c343af8SMatthew Garrett evm_dir = securityfs_create_dir("evm", integrity_dir); 2910c343af8SMatthew Garrett if (!evm_dir || IS_ERR(evm_dir)) 2920c343af8SMatthew Garrett return -EFAULT; 2930c343af8SMatthew Garrett 2940c343af8SMatthew Garrett evm_init_tpm = securityfs_create_file("evm", 0660, 2950c343af8SMatthew Garrett evm_dir, NULL, &evm_key_ops); 2960c343af8SMatthew Garrett if (!evm_init_tpm || IS_ERR(evm_init_tpm)) { 29766dbc325SMimi Zohar error = -EFAULT; 2980c343af8SMatthew Garrett goto out; 2990c343af8SMatthew Garrett } 3000c343af8SMatthew Garrett 3010c343af8SMatthew Garrett evm_symlink = securityfs_create_symlink("evm", NULL, 3020c343af8SMatthew Garrett "integrity/evm/evm", NULL); 3030c343af8SMatthew Garrett if (!evm_symlink || IS_ERR(evm_symlink)) { 3040c343af8SMatthew Garrett error = -EFAULT; 3050c343af8SMatthew Garrett goto out; 3060c343af8SMatthew Garrett } 3070c343af8SMatthew Garrett 308fa516b66SMatthew Garrett if (evm_init_xattrs() != 0) { 309fa516b66SMatthew Garrett error = -EFAULT; 310fa516b66SMatthew Garrett goto out; 311fa516b66SMatthew Garrett } 312fa516b66SMatthew Garrett 3130c343af8SMatthew Garrett return 0; 3140c343af8SMatthew Garrett out: 3150c343af8SMatthew Garrett securityfs_remove(evm_symlink); 3160c343af8SMatthew Garrett securityfs_remove(evm_init_tpm); 3170c343af8SMatthew Garrett securityfs_remove(evm_dir); 31866dbc325SMimi Zohar return error; 31966dbc325SMimi Zohar } 320