2 * Copyright (C) 2010 Novell, Inc.
3 * Written by Andreas Gruenbacher <agruen@suse.de>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2, or (at your option) any
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
16 #include <linux/sched.h>
17 #include <linux/module.h>
19 #include <linux/richacl.h>
22 * richacl_may_create - helper for implementing iop->may_create
25 richacl_may_create(struct inode *dir, int isdir,
26 int (*richacl_permission)(struct inode *, unsigned int))
29 return richacl_permission(dir,
30 ACE4_EXECUTE | (isdir ?
31 ACE4_ADD_SUBDIRECTORY : ACE4_ADD_FILE));
33 return generic_permission(dir, MAY_WRITE | MAY_EXEC);
35 EXPORT_SYMBOL(richacl_may_create);
38 check_sticky(struct inode *dir, struct inode *inode)
40 if (!(dir->i_mode & S_ISVTX))
42 if (inode->i_uid == current_fsuid())
44 if (dir->i_uid == current_fsuid())
46 return !capable(CAP_FOWNER);
50 * richacl_may_delete - helper for implementing iop->may_delete
53 richacl_may_delete(struct inode *dir, struct inode *inode, int replace,
54 int (*richacl_permission)(struct inode *, unsigned int))
58 if (IS_RICHACL(inode)) {
59 error = richacl_permission(dir,
60 ACE4_EXECUTE | ACE4_DELETE_CHILD);
61 if (!error && check_sticky(dir, inode))
63 if (error && !richacl_permission(inode, ACE4_DELETE))
65 if (!error && replace)
66 error = richacl_permission(dir,
67 ACE4_EXECUTE | (S_ISDIR(inode->i_mode) ?
68 ACE4_ADD_SUBDIRECTORY : ACE4_ADD_FILE));
70 error = generic_permission(dir, MAY_WRITE | MAY_EXEC);
71 if (!error && check_sticky(dir, inode))
77 EXPORT_SYMBOL(richacl_may_delete);
80 * richacl_inode_permission - helper for implementing iop->permission
81 * @inode: inode to check
82 * @acl: rich acl of the inode (may be NULL)
83 * @mask: requested access (ACE4_* bitmask)
85 * This function is supposed to be used by file systems for implementing the
86 * permission inode operation.
89 richacl_inode_permission(struct inode *inode, const struct richacl *acl,
93 if (!richacl_permission(inode, acl, mask))
96 int mode = inode->i_mode;
98 if (current_fsuid() == inode->i_uid)
100 else if (in_group_p(inode->i_gid))
102 if (!(mask & ~richacl_mode_to_mask(mode)))
107 * Keep in sync with the capability checks in generic_permission().
109 if (!(mask & ~ACE4_POSIX_MODE_ALL)) {
111 * Read/write DACs are always overridable.
112 * Executable DACs are overridable if at
113 * least one exec bit is set.
115 if (!(mask & ACE4_POSIX_MODE_EXEC) || execute_ok(inode))
116 if (capable(CAP_DAC_OVERRIDE))
120 * Searching includes executable on directories, else just read.
122 if (!(mask & ~(ACE4_READ_DATA | ACE4_LIST_DIRECTORY | ACE4_EXECUTE)) &&
123 (S_ISDIR(inode->i_mode) || !(mask & ACE4_EXECUTE)))
124 if (capable(CAP_DAC_READ_SEARCH))
129 EXPORT_SYMBOL_GPL(richacl_inode_permission);
132 * richacl_inode_change_ok - helper for implementing iop->setattr
133 * @inode: inode to check
134 * @attr: requested inode attribute changes
135 * @richacl_permission: permission function taking an inode and ACE4_* flags
137 * Keep in sync with inode_change_ok().
140 richacl_inode_change_ok(struct inode *inode, struct iattr *attr,
141 int (*richacl_permission)(struct inode *, unsigned int))
143 unsigned int ia_valid = attr->ia_valid;
145 /* If force is set do it anyway. */
146 if (ia_valid & ATTR_FORCE)
149 /* Make sure a caller can chown. */
150 if ((ia_valid & ATTR_UID) &&
151 (current_fsuid() != inode->i_uid ||
152 attr->ia_uid != inode->i_uid) &&
153 (current_fsuid() != attr->ia_uid ||
154 richacl_permission(inode, ACE4_WRITE_OWNER)) &&
158 /* Make sure caller can chgrp. */
159 if ((ia_valid & ATTR_GID)) {
160 int in_group = in_group_p(attr->ia_gid);
161 if ((current_fsuid() != inode->i_uid ||
162 (!in_group && attr->ia_gid != inode->i_gid)) &&
164 richacl_permission(inode, ACE4_WRITE_OWNER)) &&
169 /* Make sure a caller can chmod. */
170 if (ia_valid & ATTR_MODE) {
171 if (current_fsuid() != inode->i_uid &&
172 richacl_permission(inode, ACE4_WRITE_ACL) &&
173 !capable(CAP_FOWNER))
175 /* Also check the setgid bit! */
176 if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
177 inode->i_gid) && !capable(CAP_FSETID))
178 attr->ia_mode &= ~S_ISGID;
181 /* Check for setting the inode time. */
182 if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) {
183 if (current_fsuid() != inode->i_uid &&
184 richacl_permission(inode, ACE4_WRITE_ATTRIBUTES) &&
185 !capable(CAP_FOWNER))
192 EXPORT_SYMBOL_GPL(richacl_inode_change_ok);