[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20070926142120.2656.73358.stgit@warthog.procyon.org.uk>
Date: Wed, 26 Sep 2007 15:21:20 +0100
From: David Howells <dhowells@...hat.com>
To: viro@....linux.org.uk, hch@...radead.org,
Trond.Myklebust@...app.com, sds@...ho.nsa.gov,
casey@...aufler-ca.com
Cc: linux-kernel@...r.kernel.org, selinux@...ho.nsa.gov,
linux-security-module@...r.kernel.org, dhowells@...hat.com
Subject: [PATCH 04/24] CRED: Move the effective capabilities into the cred
struct
Move the effective capabilities mask from the task struct into the credentials
record.
Note that the effective capabilities mask in the cred struct shadows that in
the task_struct because a thread can have its capabilities masks changed by
another thread. The shadowing is performed by update_current_cred() which is
invoked on entry to any system call that might need it.
Signed-off-by: David Howells <dhowells@...hat.com>
---
fs/buffer.c | 3 +++
fs/ioprio.c | 3 +++
fs/open.c | 27 +++++++++------------------
fs/proc/array.c | 2 +-
fs/readdir.c | 3 +++
include/linux/cred.h | 2 ++
include/linux/init_task.h | 2 +-
include/linux/sched.h | 2 +-
ipc/msg.c | 3 +++
ipc/sem.c | 3 +++
ipc/shm.c | 3 +++
kernel/acct.c | 3 +++
kernel/capability.c | 3 +++
kernel/compat.c | 3 +++
kernel/cred.c | 36 +++++++++++++++++++++++++++++-------
kernel/exit.c | 2 ++
kernel/fork.c | 6 +++++-
kernel/futex.c | 3 +++
kernel/futex_compat.c | 3 +++
kernel/kexec.c | 3 +++
kernel/module.c | 6 ++++++
kernel/ptrace.c | 3 +++
kernel/sched.c | 9 +++++++++
kernel/signal.c | 6 ++++++
kernel/sys.c | 39 +++++++++++++++++++++++++++++++++++++++
kernel/sysctl.c | 3 +++
kernel/time.c | 9 +++++++++
kernel/uid16.c | 3 +++
mm/mempolicy.c | 6 ++++++
mm/migrate.c | 3 +++
mm/mlock.c | 4 ++++
mm/mmap.c | 3 +++
mm/mremap.c | 3 +++
mm/oom_kill.c | 9 +++++++--
mm/swapfile.c | 6 ++++++
net/compat.c | 6 ++++++
net/socket.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
security/commoncap.c | 32 +++++++++++++++++---------------
security/dummy.c | 22 ++++++++++++++++++----
39 files changed, 282 insertions(+), 50 deletions(-)
diff --git a/fs/buffer.c b/fs/buffer.c
index 0e5ec37..9aabf79 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2909,6 +2909,9 @@ asmlinkage long sys_bdflush(int func, long data)
{
static int msg_count;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
diff --git a/fs/ioprio.c b/fs/ioprio.c
index 10d2c21..d32b7b7 100644
--- a/fs/ioprio.c
+++ b/fs/ioprio.c
@@ -63,6 +63,9 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
struct pid *pgrp;
int ret;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
switch (class) {
case IOPRIO_CLASS_RT:
if (!capable(CAP_SYS_ADMIN))
diff --git a/fs/open.c b/fs/open.c
index 0c05863..f765ec5 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -450,7 +450,7 @@ out:
asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
{
struct nameidata nd;
- kernel_cap_t old_cap;
+ kernel_cap_t old_cap, want_cap = CAP_EMPTY_SET;
struct cred *cred;
int res;
@@ -461,33 +461,26 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
if (res < 0)
return res;
- old_cap = current->cap_effective;
+ /* Clear the capabilities if we switch to a non-root user */
+ if (!current->uid)
+ want_cap = current->cap_permitted;
+
+ old_cap = current->cred->cap_effective;
if (current->cred->uid != current->uid ||
- current->cred->gid != current->gid) {
+ current->cred->gid != current->gid ||
+ current->cred->cap_effective != want_cap) {
cred = dup_cred(current->cred);
if (!cred)
return -ENOMEM;
change_fsuid(cred, current->uid);
change_fsgid(cred, current->gid);
+ change_cap(cred, want_cap);
} else {
cred = get_current_cred();
}
- /*
- * Clear the capabilities if we switch to a non-root user
- *
- * FIXME: There is a race here against sys_capset. The
- * capabilities can change yet we will restore the old
- * value below. We should hold task_capabilities_lock,
- * but we cannot because user_path_walk can sleep.
- */
- if (current->uid)
- cap_clear(current->cap_effective);
- else
- current->cap_effective = current->cap_permitted;
-
cred = __set_current_cred(cred);
res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
if (res)
@@ -506,8 +499,6 @@ out_path_release:
path_release(&nd);
out:
set_current_cred(cred);
- current->cap_effective = old_cap;
-
return res;
}
diff --git a/fs/proc/array.c b/fs/proc/array.c
index dc2f83a..1a406c7 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -286,7 +286,7 @@ static inline char *task_cap(struct task_struct *p, char *buffer)
"CapEff:\t%016x\n",
cap_t(p->cap_inheritable),
cap_t(p->cap_permitted),
- cap_t(p->cap_effective));
+ cap_t(p->_cap_effective));
}
static inline char *task_context_switch_counts(struct task_struct *p,
diff --git a/fs/readdir.c b/fs/readdir.c
index 57e6aa9..33c69ac 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -103,6 +103,9 @@ asmlinkage long old_readdir(unsigned int fd, struct old_linux_dirent __user * di
struct file * file;
struct readdir_callback buf;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
error = -EBADF;
file = fget(fd);
if (!file)
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 7e35b2f..78924d5 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -24,6 +24,7 @@ struct cred {
atomic_t usage;
uid_t uid; /* fsuid as was */
gid_t gid; /* fsgid as was */
+ kernel_cap_t cap_effective;
struct rcu_head exterminate; /* cred destroyer */
struct group_info *group_info;
void *security;
@@ -48,6 +49,7 @@ extern void put_cred(struct cred *);
extern void change_fsuid(struct cred *, uid_t);
extern void change_fsgid(struct cred *, gid_t);
extern void change_groups(struct cred *, struct group_info *);
+extern void change_cap(struct cred *, kernel_cap_t);
extern struct cred *dup_cred(const struct cred *);
/**
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 5cb7931..56d4be3 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -141,7 +141,7 @@ extern struct nsproxy init_nsproxy;
.sibling = LIST_HEAD_INIT(tsk.sibling), \
.group_leader = &tsk, \
.cred = &init_cred, \
- .cap_effective = CAP_INIT_EFF_SET, \
+ ._cap_effective = CAP_INIT_EFF_SET, \
.cap_inheritable = CAP_INIT_INH_SET, \
.cap_permitted = CAP_FULL_SET, \
.keep_capabilities = 0, \
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ca0d553..52f2b64 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1037,7 +1037,7 @@ struct task_struct {
struct cred *cred;
uid_t uid,euid,suid;
gid_t gid,egid,sgid;
- kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
+ kernel_cap_t _cap_effective, cap_inheritable, cap_permitted;
unsigned keep_capabilities:1;
struct user_struct *user;
#ifdef CONFIG_KEYS
diff --git a/ipc/msg.c b/ipc/msg.c
index a03fcb5..a351c89 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -393,6 +393,9 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
if (msqid < 0 || cmd < 0)
return -EINVAL;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
version = ipc_parse_version(&cmd);
ns = current->nsproxy->ipc_ns;
diff --git a/ipc/sem.c b/ipc/sem.c
index b676fef..9691b40 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -927,6 +927,9 @@ asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
if (semid < 0)
return -EINVAL;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
version = ipc_parse_version(&cmd);
ns = current->nsproxy->ipc_ns;
diff --git a/ipc/shm.c b/ipc/shm.c
index a86a3a5..709a4fe 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -589,6 +589,9 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
goto out;
}
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
version = ipc_parse_version(&cmd);
ns = current->nsproxy->ipc_ns;
diff --git a/kernel/acct.c b/kernel/acct.c
index 24f0f8b..01961a5 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -253,6 +253,9 @@ asmlinkage long sys_acct(const char __user *name)
{
int error;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SYS_PACCT))
return -EPERM;
diff --git a/kernel/capability.c b/kernel/capability.c
index c8d3c77..3ae73f9 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -178,6 +178,9 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
int ret;
pid_t pid;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (get_user(version, &header->version))
return -EFAULT;
diff --git a/kernel/compat.c b/kernel/compat.c
index 3bae374..04be932 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -909,6 +909,9 @@ asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp)
struct timex txc;
int ret;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
memset(&txc, 0, sizeof(struct timex));
if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) ||
diff --git a/kernel/cred.c b/kernel/cred.c
index 9868eef..f545634 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -21,16 +21,19 @@
*/
struct cred init_cred = {
.usage = ATOMIC_INIT(2),
+ .cap_effective = CAP_INIT_EFF_SET,
.group_info = &init_groups,
};
/**
* update_current_cred - Bring the current task's creds up to date
*
- * Bring the current task's credentials up to date with respect to the keyrings
- * they shadow. The process and session level keyrings may get changed by
- * sibling threads with the same process, but the change can't be applied back
- * to this thread's cred struct except by this thread itself.
+ * Bring the current task's credential record up to date with respect to the
+ * effective capability mask and keyrings it shadows. The capabilities mask
+ * may get changed by other processes, and process and session level keyrings
+ * may get changed by sibling threads with the same process, but the change
+ * can't be applied back to this thread's cred struct except by this thread
+ * itself.
*/
int update_current_cred(void)
{
@@ -46,16 +49,21 @@ int update_current_cred(void)
key_ref_to_ptr(cred->process_keyring) == sig->process_keyring &&
key_ref_to_ptr(cred->thread_keyring) == current->thread_keyring &&
#endif
- true)
+ cred->cap_effective != current->_cap_effective)
return 0;
- cred = kmalloc(sizeof(struct cred), GFP_KERNEL);
+ cred = kmemdup(current->cred, sizeof(struct cred), GFP_KERNEL);
if (!cred)
return -ENOMEM;
- *cred = *current->cred;
+ if (security_cred_dup(cred) < 0) {
+ kfree(cred);
+ return -ENOMEM;
+ }
+
atomic_set(&cred->usage, 1);
get_group_info(cred->group_info);
+ cred->cap_effective = current->_cap_effective;
#ifdef CONFIG_KEYS
rcu_read_lock();
@@ -188,3 +196,17 @@ void change_groups(struct cred *cred, struct group_info *group_info)
}
EXPORT_SYMBOL(change_groups);
+
+/**
+ * change_cap - Change the supplementary groups in a new credential record
+ * @cred: The credential record to alter
+ * @cap: The capabilities to set
+ *
+ * Change the effective capabilities in a new credential record.
+ */
+void change_cap(struct cred *cred, kernel_cap_t cap)
+{
+ cred->cap_effective = cap;
+}
+
+EXPORT_SYMBOL(change_cap);
diff --git a/kernel/exit.c b/kernel/exit.c
index c366ae7..a9916e5 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -888,6 +888,8 @@ fastcall NORET_TYPE void do_exit(long code)
struct task_struct *tsk = current;
int group_dead;
+ update_current_cred();
+
profile_task_exit(tsk);
WARN_ON(atomic_read(&tsk->fs_excl));
diff --git a/kernel/fork.c b/kernel/fork.c
index 677c353..e2948ed 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1422,9 +1422,13 @@ long do_fork(unsigned long clone_flags,
{
struct task_struct *p;
int trace = 0;
- struct pid *pid = alloc_pid();
+ struct pid *pid;
long nr;
+ if (update_current_cred())
+ return -ENOMEM;
+
+ pid = alloc_pid();
if (!pid)
return -EAGAIN;
nr = pid->nr;
diff --git a/kernel/futex.c b/kernel/futex.c
index e8935b1..40070fe 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1846,6 +1846,9 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
struct robust_list_head __user *head;
unsigned long ret;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!pid)
head = current->robust_list;
else {
diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c
index 7e52eb0..a872029 100644
--- a/kernel/futex_compat.c
+++ b/kernel/futex_compat.c
@@ -109,6 +109,9 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
struct compat_robust_list_head __user *head;
unsigned long ret;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!pid)
head = current->compat_robust_list;
else {
diff --git a/kernel/kexec.c b/kernel/kexec.c
index 25db14b..e1feb2f 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -921,6 +921,9 @@ asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments,
int locked;
int result;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
/* We only trust the superuser with rebooting the system. */
if (!capable(CAP_SYS_BOOT))
return -EPERM;
diff --git a/kernel/module.c b/kernel/module.c
index db0ead0..32893a5 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -660,6 +660,9 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
char name[MODULE_NAME_LEN];
int ret, forced = 0;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SYS_MODULE))
return -EPERM;
@@ -1978,6 +1981,9 @@ sys_init_module(void __user *umod,
struct module *mod;
int ret = 0;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
/* Must have permission */
if (!capable(CAP_SYS_MODULE))
return -EPERM;
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 3eca7a5..15fb1ff 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -456,6 +456,9 @@ asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
struct task_struct *child;
long ret;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
/*
* This lock_kernel fixes a subtle race with suid exec
*/
diff --git a/kernel/sched.c b/kernel/sched.c
index 6107a0c..602f526 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -4063,6 +4063,9 @@ asmlinkage long sys_nice(int increment)
{
long nice, retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
/*
* Setpriority might change our priority at the same moment.
* We don't have to worry. Conceptually one call occurs first
@@ -4295,6 +4298,9 @@ do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param)
struct task_struct *p;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!param || pid < 0)
return -EINVAL;
if (copy_from_user(&lparam, param, sizeof(struct sched_param)))
@@ -4468,6 +4474,9 @@ asmlinkage long sys_sched_setaffinity(pid_t pid, unsigned int len,
cpumask_t new_mask;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
retval = get_user_cpu_mask(user_mask_ptr, len, &new_mask);
if (retval)
return retval;
diff --git a/kernel/signal.c b/kernel/signal.c
index 9fb91a3..0a3358f 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2197,6 +2197,9 @@ sys_kill(int pid, int sig)
{
struct siginfo info;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
info.si_signo = sig;
info.si_errno = 0;
info.si_code = SI_USER;
@@ -2212,6 +2215,9 @@ static int do_tkill(int tgid, int pid, int sig)
struct siginfo info;
struct task_struct *p;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
error = -ESRCH;
info.si_signo = sig;
info.si_errno = 0;
diff --git a/kernel/sys.c b/kernel/sys.c
index 9bb591f..ff34679 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -670,6 +670,9 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
int error = -EINVAL;
struct pid *pgrp;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (which > PRIO_USER || which < PRIO_PROCESS)
goto out;
@@ -896,6 +899,9 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
{
char buffer[256];
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
/* We only trust the superuser with rebooting the system. */
if (!capable(CAP_SYS_BOOT))
return -EPERM;
@@ -1019,6 +1025,9 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
int new_egid = old_egid;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
if (retval)
return retval;
@@ -1072,6 +1081,9 @@ asmlinkage long sys_setgid(gid_t gid)
int old_egid = current->egid;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
if (retval)
return retval;
@@ -1150,6 +1162,9 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
int old_ruid, old_euid, old_suid, new_ruid, new_euid;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
if (retval)
return retval;
@@ -1221,6 +1236,9 @@ asmlinkage long sys_setuid(uid_t uid)
int old_ruid, old_suid, new_suid;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
if (retval)
return retval;
@@ -1271,6 +1289,9 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
int old_suid = current->suid;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
if (retval)
return retval;
@@ -1333,6 +1354,9 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
struct cred *cred;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
if (retval)
return retval;
@@ -1876,6 +1900,9 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
struct group_info *group_info;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SETGID))
return -EPERM;
if ((unsigned)gidsetsize > NGROUPS_MAX)
@@ -1941,6 +1968,9 @@ asmlinkage long sys_sethostname(char __user *name, int len)
int errno;
char tmp[__NEW_UTS_LEN];
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (len < 0 || len > __NEW_UTS_LEN)
@@ -1986,6 +2016,9 @@ asmlinkage long sys_setdomainname(char __user *name, int len)
int errno;
char tmp[__NEW_UTS_LEN];
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (len < 0 || len > __NEW_UTS_LEN)
@@ -2045,6 +2078,9 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim)
unsigned long it_prof_secs;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (resource >= RLIM_NLIMITS)
return -EINVAL;
if (copy_from_user(&new_rlim, rlim, sizeof(*rlim)))
@@ -2226,6 +2262,9 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
{
long error;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
error = security_task_prctl(option, arg2, arg3, arg4, arg5);
if (error)
return error;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 53a456e..9447293 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1347,6 +1347,9 @@ asmlinkage long sys_sysctl(struct __sysctl_args __user *args)
struct __sysctl_args tmp;
int error;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (copy_from_user(&tmp, args, sizeof(tmp)))
return -EFAULT;
diff --git a/kernel/time.c b/kernel/time.c
index 2289a8d..975f47d 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -82,6 +82,9 @@ asmlinkage long sys_stime(time_t __user *tptr)
struct timespec tv;
int err;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (get_user(tv.tv_sec, tptr))
return -EFAULT;
@@ -186,6 +189,9 @@ asmlinkage long sys_settimeofday(struct timeval __user *tv,
struct timespec new_ts;
struct timezone new_tz;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (tv) {
if (copy_from_user(&user_tv, tv, sizeof(*tv)))
return -EFAULT;
@@ -205,6 +211,9 @@ asmlinkage long sys_adjtimex(struct timex __user *txc_p)
struct timex txc; /* Local copy of parameter */
int ret;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
/* Copy the user data space into the kernel copy
* structure. But bear in mind that the structures
* may change
diff --git a/kernel/uid16.c b/kernel/uid16.c
index 5a8b95e..5238a96 100644
--- a/kernel/uid16.c
+++ b/kernel/uid16.c
@@ -187,6 +187,9 @@ asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t __user *grouplist)
struct group_info *group_info;
int retval;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SETGID))
return -EPERM;
if ((unsigned)gidsetsize > NGROUPS_MAX)
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 3d6ac95..64cfcf2 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -878,6 +878,9 @@ asmlinkage long sys_mbind(unsigned long start, unsigned long len,
nodemask_t nodes;
int err;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
err = get_nodes(&nodes, nmask, maxnode);
if (err)
return err;
@@ -914,6 +917,9 @@ asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
nodemask_t task_nodes;
int err;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
err = get_nodes(&old, old_nodes, maxnode);
if (err)
return err;
diff --git a/mm/migrate.c b/mm/migrate.c
index e2fdbce..79a1909 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -915,6 +915,9 @@ asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages,
struct mm_struct *mm;
struct page_to_node *pm = NULL;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
/* Check flags */
if (flags & ~(MPOL_MF_MOVE|MPOL_MF_MOVE_ALL))
return -EINVAL;
diff --git a/mm/mlock.c b/mm/mlock.c
index 7b26560..67985f4 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -138,6 +138,8 @@ asmlinkage long sys_mlock(unsigned long start, size_t len)
unsigned long lock_limit;
int error = -ENOMEM;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
if (!can_do_mlock())
return -EPERM;
@@ -203,6 +205,8 @@ asmlinkage long sys_mlockall(int flags)
if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE)))
goto out;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
ret = -EPERM;
if (!can_do_mlock())
goto out;
diff --git a/mm/mmap.c b/mm/mmap.c
index 0d40e66..1b7b0ff 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -240,6 +240,9 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
unsigned long newbrk, oldbrk;
struct mm_struct *mm = current->mm;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
down_write(&mm->mmap_sem);
if (brk < mm->end_code)
diff --git a/mm/mremap.c b/mm/mremap.c
index 8ea5c24..0d49048 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -418,6 +418,9 @@ asmlinkage unsigned long sys_mremap(unsigned long addr,
{
unsigned long ret;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
down_write(¤t->mm->mmap_sem);
ret = do_mremap(addr, old_len, new_len, flags, new_addr);
up_write(¤t->mm->mmap_sem);
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f9b82ad..df5edda 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -53,6 +53,7 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
unsigned long points, cpu_time, run_time, s;
struct mm_struct *mm;
struct task_struct *child;
+ kernel_cap_t cap_effective;
task_lock(p);
mm = p->mm;
@@ -123,7 +124,11 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
* Superuser processes are usually more important, so we make it
* less likely that we kill those.
*/
- if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_ADMIN) ||
+ rcu_read_lock();
+ cap_effective = task_cred(p)->cap_effective;
+ rcu_read_unlock();
+
+ if (cap_t(cap_effective) & CAP_TO_MASK(CAP_SYS_ADMIN) ||
p->uid == 0 || p->euid == 0)
points /= 4;
@@ -133,7 +138,7 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
* tend to only have this flag set on applications they think
* of as important.
*/
- if (cap_t(p->cap_effective) & CAP_TO_MASK(CAP_SYS_RAWIO))
+ if (cap_t(cap_effective) & CAP_TO_MASK(CAP_SYS_RAWIO))
points /= 4;
/*
diff --git a/mm/swapfile.c b/mm/swapfile.c
index f071648..9539da4 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1183,6 +1183,9 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
int i, type, prev;
int err;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -1433,6 +1436,9 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
struct inode *inode = NULL;
int did_down = 0;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
spin_lock(&swap_lock);
diff --git a/net/compat.c b/net/compat.c
index d74d821..c20f404 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -483,6 +483,9 @@ asmlinkage long compat_sys_setsockopt(int fd, int level, int optname,
int err;
struct socket *sock;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (level == SOL_IPV6 && optname == IPT_SO_SET_REPLACE)
return do_netfilter_replace(fd, level, optname,
optval, optlen);
@@ -603,6 +606,9 @@ asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
int err;
struct socket *sock;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if ((sock = sockfd_lookup(fd, &err))!=NULL)
{
err = security_socket_getsockopt(sock, level,
diff --git a/net/socket.c b/net/socket.c
index 50bfeef..034d221 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1200,6 +1200,9 @@ asmlinkage long sys_socket(int family, int type, int protocol)
int retval;
struct socket *sock;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
goto out;
@@ -1228,6 +1231,9 @@ asmlinkage long sys_socketpair(int family, int type, int protocol,
int fd1, fd2, err;
struct file *newfile1, *newfile2;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
/*
* Obtain the first socket and check if the underlying protocol
* supports the socketpair call.
@@ -1323,6 +1329,9 @@ asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen)
char address[MAX_SOCK_ADDR];
int err, fput_needed;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
err = move_addr_to_kernel(umyaddr, addrlen, address);
@@ -1353,6 +1362,9 @@ asmlinkage long sys_listen(int fd, int backlog)
struct socket *sock;
int err, fput_needed;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
if ((unsigned)backlog > sysctl_somaxconn)
@@ -1387,6 +1399,9 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr,
int err, len, newfd, fput_needed;
char address[MAX_SOCK_ADDR];
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
@@ -1476,6 +1491,9 @@ asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr,
char address[MAX_SOCK_ADDR];
int err, fput_needed;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
@@ -1508,6 +1526,9 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr,
char address[MAX_SOCK_ADDR];
int len, err, fput_needed;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
@@ -1539,6 +1560,9 @@ asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr,
char address[MAX_SOCK_ADDR];
int len, err, fput_needed;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock != NULL) {
err = security_socket_getpeername(sock);
@@ -1576,6 +1600,9 @@ asmlinkage long sys_sendto(int fd, void __user *buff, size_t len,
int fput_needed;
struct file *sock_file;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock_file = fget_light(fd, &fput_needed);
err = -EBADF;
if (!sock_file)
@@ -1637,6 +1664,9 @@ asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size,
struct file *sock_file;
int fput_needed;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock_file = fget_light(fd, &fput_needed);
err = -EBADF;
if (!sock_file)
@@ -1693,6 +1723,9 @@ asmlinkage long sys_setsockopt(int fd, int level, int optname,
if (optlen < 0)
return -EINVAL;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock != NULL) {
err = security_socket_setsockopt(sock, level, optname);
@@ -1724,6 +1757,9 @@ asmlinkage long sys_getsockopt(int fd, int level, int optname,
int err, fput_needed;
struct socket *sock;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock != NULL) {
err = security_socket_getsockopt(sock, level, optname);
@@ -1753,6 +1789,9 @@ asmlinkage long sys_shutdown(int fd, int how)
int err, fput_needed;
struct socket *sock;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock != NULL) {
err = security_socket_shutdown(sock, how);
@@ -1789,6 +1828,9 @@ asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags)
int err, ctl_len, iov_size, total_len;
int fput_needed;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
err = -EFAULT;
if (MSG_CMSG_COMPAT & flags) {
if (get_compat_msghdr(&msg_sys, msg_compat))
@@ -1896,6 +1938,9 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg,
struct sockaddr __user *uaddr;
int __user *uaddr_len;
+ if (update_current_cred() < 0)
+ return -ENOMEM;
+
if (MSG_CMSG_COMPAT & flags) {
if (get_compat_msghdr(&msg_sys, msg_compat))
return -EFAULT;
diff --git a/security/commoncap.c b/security/commoncap.c
index 7520361..6a56164 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -25,7 +25,7 @@
int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
{
- NETLINK_CB(skb).eff_cap = current->cap_effective;
+ NETLINK_CB(skb).eff_cap = current->cred->cap_effective;
return 0;
}
@@ -43,7 +43,7 @@ EXPORT_SYMBOL(cap_netlink_recv);
int cap_capable (struct task_struct *tsk, int cap)
{
/* Derived from include/linux/sched.h:capable. */
- if (cap_raised(tsk->cap_effective, cap))
+ if (cap_raised(tsk->cred->cap_effective, cap))
return 0;
return -EPERM;
}
@@ -68,7 +68,9 @@ int cap_capget (struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
/* Derived from kernel/capability.c:sys_capget. */
- *effective = cap_t (target->cap_effective);
+ rcu_read_lock();
+ *effective = cap_t (task_cred(target)->cap_effective);
+ rcu_read_unlock();
*inheritable = cap_t (target->cap_inheritable);
*permitted = cap_t (target->cap_permitted);
return 0;
@@ -103,7 +105,7 @@ int cap_capset_check (struct task_struct *target, kernel_cap_t *effective,
void cap_capset_set (struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- target->cap_effective = *effective;
+ target->_cap_effective = *effective;
target->cap_inheritable = *inheritable;
target->cap_permitted = *permitted;
}
@@ -162,15 +164,15 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
}
}
- current->suid = current->euid = current->fsuid = bprm->e_uid;
- current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ current->suid = current->euid = current->cred->uid = bprm->e_uid;
+ current->sgid = current->egid = current->cred->gid = bprm->e_gid;
/* For init, we want to retain the capabilities set
* in the init_task struct. Thus we skip the usual
* capability rules */
if (!is_init(current)) {
current->cap_permitted = new_permitted;
- current->cap_effective =
+ current->_cap_effective =
cap_intersect (new_permitted, bprm->cap_effective);
}
@@ -246,13 +248,13 @@ static inline void cap_emulate_setxuid (int old_ruid, int old_euid,
(current->uid != 0 && current->euid != 0 && current->suid != 0) &&
!current->keep_capabilities) {
cap_clear (current->cap_permitted);
- cap_clear (current->cap_effective);
+ cap_clear (current->_cap_effective);
}
if (old_euid == 0 && current->euid != 0) {
- cap_clear (current->cap_effective);
+ cap_clear (current->_cap_effective);
}
if (old_euid != 0 && current->euid == 0) {
- current->cap_effective = current->cap_permitted;
+ current->_cap_effective = current->cap_permitted;
}
}
@@ -280,12 +282,12 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
*/
if (!issecure (SECURE_NO_SETUID_FIXUP)) {
- if (old_fsuid == 0 && current->fsuid != 0) {
- cap_t (current->cap_effective) &=
+ if (old_fsuid == 0 && current->cred->uid != 0) {
+ cap_t (current->_cap_effective) &=
~CAP_FS_MASK;
}
- if (old_fsuid != 0 && current->fsuid == 0) {
- cap_t (current->cap_effective) |=
+ if (old_fsuid != 0 && current->cred->uid == 0) {
+ cap_t (current->_cap_effective) |=
(cap_t (current->cap_permitted) &
CAP_FS_MASK);
}
@@ -301,7 +303,7 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
void cap_task_reparent_to_init (struct task_struct *p)
{
- p->cap_effective = CAP_INIT_EFF_SET;
+ p->_cap_effective = CAP_INIT_EFF_SET;
p->cap_inheritable = CAP_INIT_INH_SET;
p->cap_permitted = CAP_FULL_SET;
p->keep_capabilities = 0;
diff --git a/security/dummy.c b/security/dummy.c
index 187fc4b..7e52156 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -76,7 +76,13 @@ static int dummy_acct (struct file *file)
static int dummy_capable (struct task_struct *tsk, int cap)
{
- if (cap_raised (tsk->cap_effective, cap))
+ kernel_cap_t cap_effective;
+
+ rcu_read_lock();
+ cap_effective = task_cred(tsk)->cap_effective;
+ rcu_read_unlock();
+
+ if (cap_raised (cap_effective, cap))
return 0;
return -EPERM;
}
@@ -146,7 +152,12 @@ static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
change_fsuid(bprm->cred, bprm->e_uid);
change_fsgid(bprm->cred, bprm->e_gid);
- dummy_capget(current, ¤t->cap_effective, ¤t->cap_inheritable, ¤t->cap_permitted);
+ dummy_capget(current,
+ ¤t->_cap_effective,
+ ¤t->cap_inheritable,
+ ¤t->cap_permitted);
+
+ change_cap(bprm->cred, current->_cap_effective);
}
static void dummy_bprm_post_apply_creds (struct linux_binprm *bprm)
@@ -499,7 +510,10 @@ static int dummy_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
static int dummy_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
{
- dummy_capget(current, ¤t->cap_effective, ¤t->cap_inheritable, ¤t->cap_permitted);
+ dummy_capget(current,
+ ¤t->_cap_effective,
+ ¤t->cap_inheritable,
+ ¤t->cap_permitted);
return 0;
}
@@ -697,7 +711,7 @@ static int dummy_sem_semop (struct sem_array *sma,
static int dummy_netlink_send (struct sock *sk, struct sk_buff *skb)
{
- NETLINK_CB(skb).eff_cap = current->cap_effective;
+ NETLINK_CB(skb).eff_cap = current->cred->cap_effective;
return 0;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists