1 // SPDX-License-Identifier: GPL-2.0
3 * SafeSetID Linux Security Module
5 * Author: Micah Morton <mortonm@chromium.org>
7 * Copyright (C) 2018 The Chromium OS Authors.
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.
14 #include <linux/security.h>
15 #include <linux/cred.h>
19 static struct dentry *safesetid_policy_dir;
21 struct safesetid_file_entry {
23 enum safesetid_whitelist_file_write_type type;
24 struct dentry *dentry;
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},
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.
39 static int parse_safesetid_whitelist_policy(const char __user *buf,
47 const char separator[] = ":";
49 size_t first_substring_length;
53 /* Duplicate string from user memory and NULL-terminate */
54 kern_buf = memdup_user_nul(buf, len);
56 return PTR_ERR(kern_buf);
59 * Format of |buf| string should be <UID>:<UID>.
60 * Find location of ":" in kern_buf (copied from |buf|).
62 first_substring_length = strcspn(kern_buf, separator);
63 if (first_substring_length == 0 || first_substring_length == len) {
68 parent_buf = kmemdup_nul(kern_buf, first_substring_length, GFP_KERNEL);
74 ret = kstrtol(parent_buf, 0, &parsed_parent);
78 child_buf = kern_buf + first_substring_length + 1;
79 ret = kstrtol(child_buf, 0, &parsed_child);
83 *parent = make_kuid(current_user_ns(), parsed_parent);
84 if (!uid_valid(*parent)) {
89 *child = make_kuid(current_user_ns(), parsed_child);
90 if (!uid_valid(*child)) {
102 static ssize_t safesetid_file_write(struct file *file,
103 const char __user *buf,
107 struct safesetid_file_entry *file_entry =
108 file->f_inode->i_private;
113 if (!ns_capable(current_user_ns(), CAP_MAC_ADMIN))
119 switch (file_entry->type) {
120 case SAFESETID_WHITELIST_FLUSH:
121 flush_safesetid_whitelist_entries();
123 case SAFESETID_WHITELIST_ADD:
124 ret = parse_safesetid_whitelist_policy(buf, len, &parent,
129 ret = add_safesetid_whitelist_entry(parent, child);
134 pr_warn("Unknown securityfs file %d\n", file_entry->type);
138 /* Return len on success so caller won't keep trying to write */
142 static const struct file_operations safesetid_file_fops = {
143 .write = safesetid_file_write,
146 static void safesetid_shutdown_securityfs(void)
150 for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) {
151 struct safesetid_file_entry *entry =
153 securityfs_remove(entry->dentry);
154 entry->dentry = NULL;
157 securityfs_remove(safesetid_policy_dir);
158 safesetid_policy_dir = NULL;
161 static int __init safesetid_init_securityfs(void)
166 if (!safesetid_initialized)
169 safesetid_policy_dir = securityfs_create_dir("safesetid", NULL);
170 if (IS_ERR(safesetid_policy_dir)) {
171 ret = PTR_ERR(safesetid_policy_dir);
175 for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) {
176 struct safesetid_file_entry *entry =
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);
190 safesetid_shutdown_securityfs();
193 fs_initcall(safesetid_init_securityfs);