aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2011-11-14 19:24:06 -0500
committerEric W. Biederman <ebiederm@xmission.com>2012-04-07 20:02:46 -0400
commit1a48e2ac034d47ed843081c4523b63c46b46888b (patch)
treed3a32ac7ffc47b075a64701a2fd74e00bbccf84d
parent973c5914260d75292f71a4729753086b9e863d57 (diff)
userns: Replace the hard to write inode_userns with inode_capable.
This represents a change in strategy of how to handle user namespaces. Instead of tagging everything explicitly with a user namespace and bulking up all of the comparisons of uids and gids in the kernel, all uids and gids in use will have a mapping to a flat kuid and kgid spaces respectively. This allows much more of the existing logic to be preserved and in general allows for faster code. In this new and improved world we allow someone to utiliize capabilities over an inode if the inodes owner mapps into the capabilities holders user namespace and the user has capabilities in their user namespace. Which is simple and efficient. Moving the fs uid comparisons to be comparisons in a flat kuid space follows in later patches, something that is only significant if you are using user namespaces. Acked-by: Serge Hallyn <serge.hallyn@canonical.com> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
-rw-r--r--fs/inode.c6
-rw-r--r--fs/namei.c18
-rw-r--r--include/linux/capability.h2
-rw-r--r--include/linux/fs.h6
-rw-r--r--kernel/capability.c19
5 files changed, 28 insertions, 23 deletions
diff --git a/fs/inode.c b/fs/inode.c
index 9f4f5fecc09..f0c4ace408e 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1732,11 +1732,9 @@ EXPORT_SYMBOL(inode_init_owner);
1732 */ 1732 */
1733bool inode_owner_or_capable(const struct inode *inode) 1733bool inode_owner_or_capable(const struct inode *inode)
1734{ 1734{
1735 struct user_namespace *ns = inode_userns(inode); 1735 if (current_fsuid() == inode->i_uid)
1736
1737 if (current_user_ns() == ns && current_fsuid() == inode->i_uid)
1738 return true; 1736 return true;
1739 if (ns_capable(ns, CAP_FOWNER)) 1737 if (inode_capable(inode, CAP_FOWNER))
1740 return true; 1738 return true;
1741 return false; 1739 return false;
1742} 1740}
diff --git a/fs/namei.c b/fs/namei.c
index 701954d68ac..941c4362e29 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -228,9 +228,6 @@ static int acl_permission_check(struct inode *inode, int mask)
228{ 228{
229 unsigned int mode = inode->i_mode; 229 unsigned int mode = inode->i_mode;
230 230
231 if (current_user_ns() != inode_userns(inode))
232 goto other_perms;
233
234 if (likely(current_fsuid() == inode->i_uid)) 231 if (likely(current_fsuid() == inode->i_uid))
235 mode >>= 6; 232 mode >>= 6;
236 else { 233 else {
@@ -244,7 +241,6 @@ static int acl_permission_check(struct inode *inode, int mask)
244 mode >>= 3; 241 mode >>= 3;
245 } 242 }
246 243
247other_perms:
248 /* 244 /*
249 * If the DACs are ok we don't need any capability check. 245 * If the DACs are ok we don't need any capability check.
250 */ 246 */
@@ -280,10 +276,10 @@ int generic_permission(struct inode *inode, int mask)
280 276
281 if (S_ISDIR(inode->i_mode)) { 277 if (S_ISDIR(inode->i_mode)) {
282 /* DACs are overridable for directories */ 278 /* DACs are overridable for directories */
283 if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE)) 279 if (inode_capable(inode, CAP_DAC_OVERRIDE))
284 return 0; 280 return 0;
285 if (!(mask & MAY_WRITE)) 281 if (!(mask & MAY_WRITE))
286 if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH)) 282 if (inode_capable(inode, CAP_DAC_READ_SEARCH))
287 return 0; 283 return 0;
288 return -EACCES; 284 return -EACCES;
289 } 285 }
@@ -293,7 +289,7 @@ int generic_permission(struct inode *inode, int mask)
293 * at least one exec bit set. 289 * at least one exec bit set.
294 */ 290 */
295 if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO)) 291 if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
296 if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE)) 292 if (inode_capable(inode, CAP_DAC_OVERRIDE))
297 return 0; 293 return 0;
298 294
299 /* 295 /*
@@ -301,7 +297,7 @@ int generic_permission(struct inode *inode, int mask)
301 */ 297 */
302 mask &= MAY_READ | MAY_WRITE | MAY_EXEC; 298 mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
303 if (mask == MAY_READ) 299 if (mask == MAY_READ)
304 if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH)) 300 if (inode_capable(inode, CAP_DAC_READ_SEARCH))
305 return 0; 301 return 0;
306 302
307 return -EACCES; 303 return -EACCES;
@@ -1964,15 +1960,11 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)
1964 1960
1965 if (!(dir->i_mode & S_ISVTX)) 1961 if (!(dir->i_mode & S_ISVTX))
1966 return 0; 1962 return 0;
1967 if (current_user_ns() != inode_userns(inode))
1968 goto other_userns;
1969 if (inode->i_uid == fsuid) 1963 if (inode->i_uid == fsuid)
1970 return 0; 1964 return 0;
1971 if (dir->i_uid == fsuid) 1965 if (dir->i_uid == fsuid)
1972 return 0; 1966 return 0;
1973 1967 return !inode_capable(inode, CAP_FOWNER);
1974other_userns:
1975 return !ns_capable(inode_userns(inode), CAP_FOWNER);
1976} 1968}
1977 1969
1978/* 1970/*
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 12d52dedb22..a76eca90747 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -374,6 +374,7 @@ struct cpu_vfs_cap_data {
374 374
375#ifdef __KERNEL__ 375#ifdef __KERNEL__
376 376
377struct inode;
377struct dentry; 378struct dentry;
378struct user_namespace; 379struct user_namespace;
379 380
@@ -548,6 +549,7 @@ extern bool has_ns_capability_noaudit(struct task_struct *t,
548extern bool capable(int cap); 549extern bool capable(int cap);
549extern bool ns_capable(struct user_namespace *ns, int cap); 550extern bool ns_capable(struct user_namespace *ns, int cap);
550extern bool nsown_capable(int cap); 551extern bool nsown_capable(int cap);
552extern bool inode_capable(const struct inode *inode, int cap);
551 553
552/* audit system wants to get cap info from files as well */ 554/* audit system wants to get cap info from files as well */
553extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); 555extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 135693e79f2..a6c5efbee0d 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1522,12 +1522,6 @@ enum {
1522#define vfs_check_frozen(sb, level) \ 1522#define vfs_check_frozen(sb, level) \
1523 wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level))) 1523 wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level)))
1524 1524
1525/*
1526 * until VFS tracks user namespaces for inodes, just make all files
1527 * belong to init_user_ns
1528 */
1529extern struct user_namespace init_user_ns;
1530#define inode_userns(inode) (&init_user_ns)
1531extern bool inode_owner_or_capable(const struct inode *inode); 1525extern bool inode_owner_or_capable(const struct inode *inode);
1532 1526
1533/* not quite ready to be deprecated, but... */ 1527/* not quite ready to be deprecated, but... */
diff --git a/kernel/capability.c b/kernel/capability.c
index 3f1adb6c647..cc5f0718215 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -419,3 +419,22 @@ bool nsown_capable(int cap)
419{ 419{
420 return ns_capable(current_user_ns(), cap); 420 return ns_capable(current_user_ns(), cap);
421} 421}
422
423/**
424 * inode_capable - Check superior capability over inode
425 * @inode: The inode in question
426 * @cap: The capability in question
427 *
428 * Return true if the current task has the given superior capability
429 * targeted at it's own user namespace and that the given inode is owned
430 * by the current user namespace or a child namespace.
431 *
432 * Currently inodes can only be owned by the initial user namespace.
433 *
434 */
435bool inode_capable(const struct inode *inode, int cap)
436{
437 struct user_namespace *ns = current_user_ns();
438
439 return ns_capable(ns, cap) && (ns == &init_user_ns);
440}