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 <[EMAIL PROTECTED]>
---

 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(&current->mm->mmap_sem);
        ret = do_mremap(addr, old_len, new_len, flags, new_addr);
        up_write(&current->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, &current->cap_effective, 
&current->cap_inheritable, &current->cap_permitted);
+       dummy_capget(current,
+                    &current->_cap_effective,
+                    &current->cap_inheritable,
+                    &current->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, &current->cap_effective, 
&current->cap_inheritable, &current->cap_permitted);
+       dummy_capget(current,
+                    &current->_cap_effective,
+                    &current->cap_inheritable,
+                    &current->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-security-module" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to