1/* 2 * linux/kernel/capability.c 3 * 4 * Copyright (C) 1997 Andrew Main <zefram@fysh.org> 5 * Integrated into 2.1.97+, Andrew G. Morgan <morgan@transmeta.com> 6 */ 7 8#include <linux/mm.h> 9#include <asm/uaccess.h> 10 11kernel_cap_t cap_bset = CAP_INIT_EFF_SET; 12 13/* Note: never hold tasklist_lock while spinning for this one */ 14spinlock_t task_capability_lock = SPIN_LOCK_UNLOCKED; 15 16/* 17 * For sys_getproccap() and sys_setproccap(), any of the three 18 * capability set pointers may be NULL -- indicating that that set is 19 * uninteresting and/or not to be changed. 20 */ 21 22asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) 23{ 24 int error, pid; 25 __u32 version; 26 struct task_struct *target; 27 struct __user_cap_data_struct data; 28 29 if (get_user(version, &header->version)) 30 return -EFAULT; 31 32 error = -EINVAL; 33 if (version != _LINUX_CAPABILITY_VERSION) { 34 version = _LINUX_CAPABILITY_VERSION; 35 if (put_user(version, &header->version)) 36 error = -EFAULT; 37 return error; 38 } 39 40 if (get_user(pid, &header->pid)) 41 return -EFAULT; 42 43 if (pid < 0) 44 return -EINVAL; 45 46 error = 0; 47 48 spin_lock(&task_capability_lock); 49 50 if (pid && pid != current->pid) { 51 read_lock(&tasklist_lock); 52 target = find_task_by_pid(pid); /* identify target of query */ 53 if (!target) 54 error = -ESRCH; 55 } else { 56 target = current; 57 } 58 59 if (!error) { 60 data.permitted = cap_t(target->cap_permitted); 61 data.inheritable = cap_t(target->cap_inheritable); 62 data.effective = cap_t(target->cap_effective); 63 } 64 65 if (target != current) 66 read_unlock(&tasklist_lock); 67 spin_unlock(&task_capability_lock); 68 69 if (!error) { 70 if (copy_to_user(dataptr, &data, sizeof data)) 71 return -EFAULT; 72 } 73 74 return error; 75} 76 77/* set capabilities for all processes in a given process group */ 78 79static void cap_set_pg(int pgrp, 80 kernel_cap_t *effective, 81 kernel_cap_t *inheritable, 82 kernel_cap_t *permitted) 83{ 84 struct task_struct *target; 85 86 read_lock(&tasklist_lock); 87 for_each_task(target) { 88 if (target->pgrp != pgrp) 89 continue; 90 target->cap_effective = *effective; 91 target->cap_inheritable = *inheritable; 92 target->cap_permitted = *permitted; 93 } 94 read_unlock(&tasklist_lock); 95} 96 97/* set capabilities for all processes other than 1 and self */ 98 99static void cap_set_all(kernel_cap_t *effective, 100 kernel_cap_t *inheritable, 101 kernel_cap_t *permitted) 102{ 103 struct task_struct *target; 104 105 read_lock(&tasklist_lock); 106 /* ALL means everyone other than self or 'init' */ 107 for_each_task(target) { 108 if (target == current || target->pid == 1) 109 continue; 110 target->cap_effective = *effective; 111 target->cap_inheritable = *inheritable; 112 target->cap_permitted = *permitted; 113 } 114 read_unlock(&tasklist_lock); 115} 116 117/* 118 * The restrictions on setting capabilities are specified as: 119 * 120 * [pid is for the 'target' task. 'current' is the calling task.] 121 * 122 * I: any raised capabilities must be a subset of the (old current) Permitted 123 * P: any raised capabilities must be a subset of the (old current) permitted 124 * E: must be set to a subset of (new target) Permitted 125 */ 126 127asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) 128{ 129 kernel_cap_t inheritable, permitted, effective; 130 __u32 version; 131 struct task_struct *target; 132 int error, pid; 133 134 if (get_user(version, &header->version)) 135 return -EFAULT; 136 137 if (version != _LINUX_CAPABILITY_VERSION) { 138 version = _LINUX_CAPABILITY_VERSION; 139 if (put_user(version, &header->version)) 140 return -EFAULT; 141 return -EINVAL; 142 } 143 144 if (get_user(pid, &header->pid)) 145 return -EFAULT; 146 147 if (pid && !capable(CAP_SETPCAP)) 148 return -EPERM; 149 150 if (copy_from_user(&effective, &data->effective, sizeof(effective)) || 151 copy_from_user(&inheritable, &data->inheritable, sizeof(inheritable)) || 152 copy_from_user(&permitted, &data->permitted, sizeof(permitted))) 153 return -EFAULT; 154 155 error = -EPERM; 156 spin_lock(&task_capability_lock); 157 158 if (pid > 0 && pid != current->pid) { 159 read_lock(&tasklist_lock); 160 target = find_task_by_pid(pid); /* identify target of query */ 161 if (!target) { 162 error = -ESRCH; 163 goto out; 164 } 165 } else { 166 target = current; 167 } 168 169 170 /* verify restrictions on target's new Inheritable set */ 171 if (!cap_issubset(inheritable, 172 cap_combine(target->cap_inheritable, 173 current->cap_permitted))) { 174 goto out; 175 } 176 177 /* verify restrictions on target's new Permitted set */ 178 if (!cap_issubset(permitted, 179 cap_combine(target->cap_permitted, 180 current->cap_permitted))) { 181 goto out; 182 } 183 184 /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ 185 if (!cap_issubset(effective, permitted)) { 186 goto out; 187 } 188 189 /* having verified that the proposed changes are legal, 190 we now put them into effect. */ 191 error = 0; 192 193 if (pid < 0) { 194 if (pid == -1) /* all procs other than current and init */ 195 cap_set_all(&effective, &inheritable, &permitted); 196 197 else /* all procs in process group */ 198 cap_set_pg(-pid, &effective, &inheritable, &permitted); 199 goto spin_out; 200 } else { 201 target->cap_effective = effective; 202 target->cap_inheritable = inheritable; 203 target->cap_permitted = permitted; 204 } 205 206out: 207 if (target != current) { 208 read_unlock(&tasklist_lock); 209 } 210spin_out: 211 spin_unlock(&task_capability_lock); 212 return error; 213} 214