/*
       * linux/kernel/capability.c
       *
       * Copyright (C) 1997  Andrew Main <zefram@fysh.org>
       * Integrated into 2.1.97+,  Andrew G. Morgan <morgan@transmeta.com>
       */ 
      
      #include <linux/mm.h>
      #include <asm/uaccess.h>
      
      kernel_cap_t cap_bset = CAP_INIT_EFF_SET;
      
      /* Note: never hold tasklist_lock while spinning for this one */
      spinlock_t task_capability_lock = SPIN_LOCK_UNLOCKED;
      
      /*
       * For sys_getproccap() and sys_setproccap(), any of the three
       * capability set pointers may be NULL -- indicating that that set is
       * uninteresting and/or not to be changed.
       */
      
  22  asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
      {
           int error, pid;
           __u32 version;
           struct task_struct *target;
           struct __user_cap_data_struct data;
      
  29       if (get_user(version, &header->version))
  30  	     return -EFAULT;
      	     
           error = -EINVAL; 
  33       if (version != _LINUX_CAPABILITY_VERSION) {
                   version = _LINUX_CAPABILITY_VERSION;
  35  	     if (put_user(version, &header->version))
      		     error = -EFAULT; 
  37               return error;
           }
      
  40       if (get_user(pid, &header->pid))
  41  	     return -EFAULT; 
      
  43       if (pid < 0) 
  44               return -EINVAL;
      
           error = 0;
      
           spin_lock(&task_capability_lock);
      
  50       if (pid && pid != current->pid) {
      	     read_lock(&tasklist_lock); 
                   target = find_task_by_pid(pid);  /* identify target of query */
  53               if (!target) 
                           error = -ESRCH;
  55       } else {
                   target = current;
           }
      
  59       if (!error) { 
      	     data.permitted = cap_t(target->cap_permitted);
      	     data.inheritable = cap_t(target->cap_inheritable); 
      	     data.effective = cap_t(target->cap_effective);
           }
      
  65       if (target != current)
  66  	     read_unlock(&tasklist_lock); 
  67       spin_unlock(&task_capability_lock);
      
  69       if (!error) {
  70  	     if (copy_to_user(dataptr, &data, sizeof data))
  71  		     return -EFAULT; 
           }
      
  74       return error;
      }
      
      /* set capabilities for all processes in a given process group */
      
  79  static void cap_set_pg(int pgrp,
                          kernel_cap_t *effective,
                          kernel_cap_t *inheritable,
                          kernel_cap_t *permitted)
      {
           struct task_struct *target;
      
           /* FIXME: do we need to have a write lock here..? */
           read_lock(&tasklist_lock);
  88       for_each_task(target) {
  89               if (target->pgrp != pgrp)
  90                       continue;
                   target->cap_effective   = *effective;
                   target->cap_inheritable = *inheritable;
                   target->cap_permitted   = *permitted;
           }
  95       read_unlock(&tasklist_lock);
      }
      
      /* set capabilities for all processes other than 1 and self */
      
 100  static void cap_set_all(kernel_cap_t *effective,
                           kernel_cap_t *inheritable,
                           kernel_cap_t *permitted)
      {
           struct task_struct *target;
      
           /* FIXME: do we need to have a write lock here..? */
           read_lock(&tasklist_lock);
           /* ALL means everyone other than self or 'init' */
 109       for_each_task(target) {
 110               if (target == current || target->pid == 1)
 111                       continue;
                   target->cap_effective   = *effective;
                   target->cap_inheritable = *inheritable;
                   target->cap_permitted   = *permitted;
           }
 116       read_unlock(&tasklist_lock);
      }
      
      /*
       * The restrictions on setting capabilities are specified as:
       *
       * [pid is for the 'target' task.  'current' is the calling task.]
       *
       * I: any raised capabilities must be a subset of the (old current) Permitted
       * P: any raised capabilities must be a subset of the (old current) permitted
       * E: must be set to a subset of (new target) Permitted
       */
      
 129  asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
      {
           kernel_cap_t inheritable, permitted, effective;
           __u32 version;
           struct task_struct *target;
           int error, pid;
      
 136       if (get_user(version, &header->version))
 137  	     return -EFAULT; 
      
 139       if (version != _LINUX_CAPABILITY_VERSION) {
                   version = _LINUX_CAPABILITY_VERSION;
 141  	     if (put_user(version, &header->version))
 142  		     return -EFAULT; 
 143               return -EINVAL;
           }
      
 146       if (get_user(pid, &header->pid))
 147  	     return -EFAULT; 
      
 149       if (pid && !capable(CAP_SETPCAP))
 150               return -EPERM;
      
           if (copy_from_user(&effective, &data->effective, sizeof(effective)) ||
      	 copy_from_user(&inheritable, &data->inheritable, sizeof(inheritable)) ||
 154  	 copy_from_user(&permitted, &data->permitted, sizeof(permitted)))
 155  	     return -EFAULT; 
      
           error = -EPERM;
           spin_lock(&task_capability_lock);
      
 160       if (pid > 0 && pid != current->pid) {
                   read_lock(&tasklist_lock);
                   target = find_task_by_pid(pid);  /* identify target of query */
 163               if (!target) {
                           error = -ESRCH;
 165  		     goto out;
      	     }
 167       } else {
                   target = current;
           }
      
      
           /* verify restrictions on target's new Inheritable set */
           if (!cap_issubset(inheritable,
                             cap_combine(target->cap_inheritable,
 175                                     current->cap_permitted))) {
 176               goto out;
           }
      
           /* verify restrictions on target's new Permitted set */
           if (!cap_issubset(permitted,
                             cap_combine(target->cap_permitted,
 182                                     current->cap_permitted))) {
 183               goto out;
           }
      
           /* verify the _new_Effective_ is a subset of the _new_Permitted_ */
 187       if (!cap_issubset(effective, permitted)) {
 188               goto out;
           }
      
           /* having verified that the proposed changes are legal,
                 we now put them into effect. */
           error = 0;
      
 195       if (pid < 0) {
 196               if (pid == -1)  /* all procs other than current and init */
                           cap_set_all(&effective, &inheritable, &permitted);
      
 199               else            /* all procs in process group */
                           cap_set_pg(-pid, &effective, &inheritable, &permitted);
 201               goto spin_out;
 202       } else {
                   /* FIXME: do we need to have a write lock here..? */
                   target->cap_effective   = effective;
                   target->cap_inheritable = inheritable;
                   target->cap_permitted   = permitted;
           }
      
      out:
 210       if (target != current) {
 211               read_unlock(&tasklist_lock);
           }
      spin_out:
 214       spin_unlock(&task_capability_lock);
 215       return error;
      }