1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * SafeSetID Linux Security Module 4 * 5 * Author: Micah Morton <mortonm@chromium.org> 6 * 7 * Copyright (C) 2018 The Chromium OS Authors. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2, as 11 * published by the Free Software Foundation. 12 * 13 */ 14 #include <linux/security.h> 15 #include <linux/cred.h> 16 17 #include "lsm.h" 18 19 static struct dentry *safesetid_policy_dir; 20 21 struct safesetid_file_entry { 22 const char *name; 23 enum safesetid_whitelist_file_write_type type; 24 struct dentry *dentry; 25 }; 26 27 static struct safesetid_file_entry safesetid_files[] = { 28 {.name = "add_whitelist_policy", 29 .type = SAFESETID_WHITELIST_ADD}, 30 {.name = "flush_whitelist_policies", 31 .type = SAFESETID_WHITELIST_FLUSH}, 32 }; 33 34 /* 35 * In the case the input buffer contains one or more invalid UIDs, the kuid_t 36 * variables pointed to by 'parent' and 'child' will get updated but this 37 * function will return an error. 38 */ 39 static int parse_safesetid_whitelist_policy(const char __user *buf, 40 size_t len, 41 kuid_t *parent, 42 kuid_t *child) 43 { 44 char *kern_buf; 45 char *parent_buf; 46 char *child_buf; 47 const char separator[] = ":"; 48 int ret; 49 size_t first_substring_length; 50 long parsed_parent; 51 long parsed_child; 52 53 /* Duplicate string from user memory and NULL-terminate */ 54 kern_buf = memdup_user_nul(buf, len); 55 if (IS_ERR(kern_buf)) 56 return PTR_ERR(kern_buf); 57 58 /* 59 * Format of |buf| string should be <UID>:<UID>. 60 * Find location of ":" in kern_buf (copied from |buf|). 61 */ 62 first_substring_length = strcspn(kern_buf, separator); 63 if (first_substring_length == 0 || first_substring_length == len) { 64 ret = -EINVAL; 65 goto free_kern; 66 } 67 68 parent_buf = kmemdup_nul(kern_buf, first_substring_length, GFP_KERNEL); 69 if (!parent_buf) { 70 ret = -ENOMEM; 71 goto free_kern; 72 } 73 74 ret = kstrtol(parent_buf, 0, &parsed_parent); 75 if (ret) 76 goto free_both; 77 78 child_buf = kern_buf + first_substring_length + 1; 79 ret = kstrtol(child_buf, 0, &parsed_child); 80 if (ret) 81 goto free_both; 82 83 *parent = make_kuid(current_user_ns(), parsed_parent); 84 if (!uid_valid(*parent)) { 85 ret = -EINVAL; 86 goto free_both; 87 } 88 89 *child = make_kuid(current_user_ns(), parsed_child); 90 if (!uid_valid(*child)) { 91 ret = -EINVAL; 92 goto free_both; 93 } 94 95 free_both: 96 kfree(parent_buf); 97 free_kern: 98 kfree(kern_buf); 99 return ret; 100 } 101 102 static ssize_t safesetid_file_write(struct file *file, 103 const char __user *buf, 104 size_t len, 105 loff_t *ppos) 106 { 107 struct safesetid_file_entry *file_entry = 108 file->f_inode->i_private; 109 kuid_t parent; 110 kuid_t child; 111 int ret; 112 113 if (!ns_capable(current_user_ns(), CAP_MAC_ADMIN)) 114 return -EPERM; 115 116 if (*ppos != 0) 117 return -EINVAL; 118 119 switch (file_entry->type) { 120 case SAFESETID_WHITELIST_FLUSH: 121 flush_safesetid_whitelist_entries(); 122 break; 123 case SAFESETID_WHITELIST_ADD: 124 ret = parse_safesetid_whitelist_policy(buf, len, &parent, 125 &child); 126 if (ret) 127 return ret; 128 129 ret = add_safesetid_whitelist_entry(parent, child); 130 if (ret) 131 return ret; 132 break; 133 default: 134 pr_warn("Unknown securityfs file %d\n", file_entry->type); 135 break; 136 } 137 138 /* Return len on success so caller won't keep trying to write */ 139 return len; 140 } 141 142 static const struct file_operations safesetid_file_fops = { 143 .write = safesetid_file_write, 144 }; 145 146 static void safesetid_shutdown_securityfs(void) 147 { 148 int i; 149 150 for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) { 151 struct safesetid_file_entry *entry = 152 &safesetid_files[i]; 153 securityfs_remove(entry->dentry); 154 entry->dentry = NULL; 155 } 156 157 securityfs_remove(safesetid_policy_dir); 158 safesetid_policy_dir = NULL; 159 } 160 161 static int __init safesetid_init_securityfs(void) 162 { 163 int i; 164 int ret; 165 166 if (!safesetid_initialized) 167 return 0; 168 169 safesetid_policy_dir = securityfs_create_dir("safesetid", NULL); 170 if (IS_ERR(safesetid_policy_dir)) { 171 ret = PTR_ERR(safesetid_policy_dir); 172 goto error; 173 } 174 175 for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) { 176 struct safesetid_file_entry *entry = 177 &safesetid_files[i]; 178 entry->dentry = securityfs_create_file( 179 entry->name, 0200, safesetid_policy_dir, 180 entry, &safesetid_file_fops); 181 if (IS_ERR(entry->dentry)) { 182 ret = PTR_ERR(entry->dentry); 183 goto error; 184 } 185 } 186 187 return 0; 188 189 error: 190 safesetid_shutdown_securityfs(); 191 return ret; 192 } 193 fs_initcall(safesetid_init_securityfs); 194