[Devel] [PATCH 4/6] user ns: hook generic_permission()
Serge E. Hallyn
serue at us.ibm.com
Mon Jun 4 12:41:44 PDT 2007
>From nobody Mon Sep 17 00:00:00 2001
From: Serge E. Hallyn <serue at us.ibm.com>
Date: Thu, 5 Apr 2007 17:17:23 -0400
Subject: [PATCH 4/6] user ns: hook generic_permission()
Hook generic_permission() to check for user namespaces.
Also define task_ino_capable() which denies a capability
if the subject task is not in the target inode's user
namespace.
Once user namespace keys exist, it will be possible to
grant a user a capability for a specific user namespace.
We will also need to do a user namespace equiv check for
capabilities with a target task.
Signed-off-by: Serge E. Hallyn <serue at us.ibm.com>
---
fs/namei.c | 33 ++++++++++++++++----------
include/linux/user_namespace.h | 51 ++++++++++++++++++++++++++++++++++++++--
kernel/user_namespace.c | 8 ------
3 files changed, 70 insertions(+), 22 deletions(-)
ef97a7bb9846d40108cab755ceec37b012b4bcf6
diff --git a/fs/namei.c b/fs/namei.c
index 6326faf..f4fa166 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -32,6 +32,7 @@ #include <linux/capability.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/namei.h>
+#include <linux/user_namespace.h>
#include <asm/namei.h>
#include <asm/uaccess.h>
@@ -183,8 +184,9 @@ int generic_permission(struct inode *ino
int (*check_acl)(struct inode *inode, int mask))
{
umode_t mode = inode->i_mode;
+ int sameuserns = task_inode_same_userns(current, inode);
- if (current->fsuid == inode->i_uid)
+ if (sameuserns && current->fsuid == inode->i_uid)
mode >>= 6;
else {
if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
@@ -195,7 +197,7 @@ int generic_permission(struct inode *ino
return error;
}
- if (in_group_p(inode->i_gid))
+ if (sameuserns && in_group_p(inode->i_gid))
mode >>= 3;
}
@@ -212,14 +214,14 @@ int generic_permission(struct inode *ino
*/
if (!(mask & MAY_EXEC) ||
(inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
- if (capable(CAP_DAC_OVERRIDE))
+ if (task_ino_capable(inode, CAP_DAC_OVERRIDE))
return 0;
/*
* Searching includes executable on directories, else just read.
*/
if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
- if (capable(CAP_DAC_READ_SEARCH))
+ if (task_ino_capable(inode, CAP_DAC_READ_SEARCH))
return 0;
return -EACCES;
@@ -436,21 +438,25 @@ static int exec_permission_lite(struct i
if (inode->i_op && inode->i_op->permission)
return -EAGAIN;
+ if (!task_inode_same_userns(current, inode))
+ goto different_userns;
+
if (current->fsuid == inode->i_uid)
mode >>= 6;
else if (in_group_p(inode->i_gid))
mode >>= 3;
+different_userns:
if (mode & MAY_EXEC)
goto ok;
- if ((inode->i_mode & S_IXUGO) && capable(CAP_DAC_OVERRIDE))
+ if ((inode->i_mode & S_IXUGO) && task_ino_capable(inode, CAP_DAC_OVERRIDE))
goto ok;
- if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_OVERRIDE))
+ if (S_ISDIR(inode->i_mode) && task_ino_capable(inode, CAP_DAC_OVERRIDE))
goto ok;
- if (S_ISDIR(inode->i_mode) && capable(CAP_DAC_READ_SEARCH))
+ if (S_ISDIR(inode->i_mode) && task_ino_capable(inode, CAP_DAC_READ_SEARCH))
goto ok;
return -EACCES;
@@ -1339,16 +1345,19 @@ int fastcall __user_walk(const char __us
/*
* It's inline, so penalty for filesystems that don't use sticky bit is
* minimal.
+ *
+ * Alas, the task_inode_same_fsuid() calls are starting to change that.
*/
static inline int check_sticky(struct inode *dir, struct inode *inode)
{
if (!(dir->i_mode & S_ISVTX))
return 0;
- if (inode->i_uid == current->fsuid)
+ if (task_inode_same_fsuid(current, inode))
return 0;
- if (dir->i_uid == current->fsuid)
+ if (task_inode_same_fsuid(current, dir))
return 0;
- return !capable(CAP_FOWNER);
+
+ return !task_ino_capable(inode, CAP_FOWNER);
}
/*
@@ -1547,7 +1556,7 @@ int may_open(struct nameidata *nd, int a
/* O_NOATIME can only be set by the owner or superuser */
if (flag & O_NOATIME)
- if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
+ if (!task_inode_same_fsuid(current, inode) && !task_ino_capable(inode, CAP_FOWNER))
return -EPERM;
/*
@@ -1832,7 +1841,7 @@ int vfs_mknod(struct inode *dir, struct
if (error)
return error;
- if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
+ if ((S_ISCHR(mode) || S_ISBLK(mode)) && !task_ino_capable(dir, CAP_MKNOD))
return -EPERM;
if (!dir->i_op || !dir->i_op->mknod)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 3b5e040..073f3e0 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -5,6 +5,7 @@ #include <linux/kref.h>
#include <linux/nsproxy.h>
#include <linux/sched.h>
#include <linux/err.h>
+#include <linux/fs.h>
#define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 8)
#define UIDHASH_SZ (1 << UIDHASH_BITS)
@@ -37,7 +38,6 @@ static inline void put_user_ns(struct us
}
struct user_namespace *task_user_ns(struct task_struct *tsk);
-struct user_namespace *inode_user_ns(struct inode *inode);
/*
* task_inode_same_userns:
* If inode->i_userns is NULL, the fs does not support user namespaces, so any
@@ -51,10 +51,43 @@ struct user_namespace *inode_user_ns(str
static inline int
task_inode_same_userns(struct task_struct *tsk, struct inode *inode)
{
- struct user_namespace *ino_userns = inode_user_ns(inode);
+ struct user_namespace *ino_userns = inode->i_userns;
return (!ino_userns || task_user_ns(tsk)== ino_userns);
}
+
+/*
+ * task_inode_same_fsuid:
+ * Does the inode's user equal the task's fsuid.
+ * Same behavior applies as for task_inode_same_userns() above.
+ * So once userns keys are implemented in the keyring, this will
+ * be modified check whether (inode->i_userns, inode->i_uid) is in
+ * tsk->user's keychain.
+ */
+static inline int
+task_inode_same_fsuid(struct task_struct *tsk, struct inode *inode)
+{
+ struct user_namespace *ino_userns = inode->i_userns;
+
+ if (ino_userns && task_user_ns(tsk) != ino_userns)
+ return 0;
+ if (tsk->fsuid != inode->i_uid)
+ return 0;
+ return 1;
+}
+
+/*
+ * task_ino_capable:
+ * Again, when userns keys exist, we will need to check for a
+ * (inode->i_userns, cap) key if !task_inode_same_userns(current, inode)
+ */
+static inline int
+task_ino_capable(struct inode *inode, int cap)
+{
+ if (!task_inode_same_userns(current, inode))
+ return 0;
+ return capable(cap);
+}
#else
static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
@@ -80,6 +113,20 @@ task_inode_same_userns(struct task_struc
{
return 1;
}
+
+static inline int
+task_inode_same_fsuid(struct task_struct *tsk, struct inode *inode)
+{
+ if (tsk->fsuid != inode->i_uid)
+ return 0;
+ return 1;
+}
+
+static inline int
+task_ino_capable(struct inode *inode, int cap)
+{
+ return capable(cap);
+}
#endif
#endif /* _LINUX_USER_H */
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index f487361..ffabd04 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -94,12 +94,4 @@ struct user_namespace *get_task_user_ns(
return get_user_ns(task_user_ns(tsk));
}
-/* should this just go into fs.h? */
-#include <linux/fs.h>
-struct user_namespace *inode_user_ns(struct inode *inode)
-{
- return inode->i_userns;
-}
-
-
#endif /* CONFIG_USER_NS */
--
1.3.2
More information about the Devel
mailing list