aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2008-07-24 00:28:24 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-24 13:47:22 -0400
commit5459c164f0591ee75ed0203bb8f3817f25948e2f (patch)
tree7b17a0cbadfc487d7311b7f5a41779ff33d6fe7f
parent78ecba081224a2db5876b6b81cfed0b78f58adc7 (diff)
security: protect legacy applications from executing with insufficient privilege
When cap_bset suppresses some of the forced (fP) capabilities of a file, it is generally only safe to execute the program if it understands how to recognize it doesn't have enough privilege to work correctly. For legacy applications (fE!=0), which have no non-destructive way to determine that they are missing privilege, we fail to execute (EPERM) any executable that requires fP capabilities, but would otherwise get pP' < fP. This is a fail-safe permission check. For some discussion of why it is problematic for (legacy) privileged applications to run with less than the set of capabilities requested for them, see: http://userweb.kernel.org/~morgan/sendmail-capabilities-war-story.html With this iteration of this support, we do not include setuid-0 based privilege protection from the bounding set. That is, the admin can still (ab)use the bounding set to suppress the privileges of a setuid-0 program. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: cleanup] Signed-off-by: Andrew G. Morgan <morgan@kernel.org> Acked-by: Serge Hallyn <serue@us.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/binfmts.h2
-rw-r--r--security/commoncap.c108
2 files changed, 60 insertions, 50 deletions
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index ee0ed48e8348..826f62350805 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -38,7 +38,7 @@ struct linux_binprm{
38 misc_bang:1; 38 misc_bang:1;
39 struct file * file; 39 struct file * file;
40 int e_uid, e_gid; 40 int e_uid, e_gid;
41 kernel_cap_t cap_inheritable, cap_permitted; 41 kernel_cap_t cap_post_exec_permitted;
42 bool cap_effective; 42 bool cap_effective;
43 void *security; 43 void *security;
44 int argc, envc; 44 int argc, envc;
diff --git a/security/commoncap.c b/security/commoncap.c
index 0b6537a3672d..4afbece37a08 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -162,8 +162,7 @@ void cap_capset_set (struct task_struct *target, kernel_cap_t *effective,
162 162
163static inline void bprm_clear_caps(struct linux_binprm *bprm) 163static inline void bprm_clear_caps(struct linux_binprm *bprm)
164{ 164{
165 cap_clear(bprm->cap_inheritable); 165 cap_clear(bprm->cap_post_exec_permitted);
166 cap_clear(bprm->cap_permitted);
167 bprm->cap_effective = false; 166 bprm->cap_effective = false;
168} 167}
169 168
@@ -198,6 +197,7 @@ static inline int cap_from_disk(struct vfs_cap_data *caps,
198{ 197{
199 __u32 magic_etc; 198 __u32 magic_etc;
200 unsigned tocopy, i; 199 unsigned tocopy, i;
200 int ret;
201 201
202 if (size < sizeof(magic_etc)) 202 if (size < sizeof(magic_etc))
203 return -EINVAL; 203 return -EINVAL;
@@ -225,19 +225,40 @@ static inline int cap_from_disk(struct vfs_cap_data *caps,
225 bprm->cap_effective = false; 225 bprm->cap_effective = false;
226 } 226 }
227 227
228 for (i = 0; i < tocopy; ++i) { 228 ret = 0;
229 bprm->cap_permitted.cap[i] = 229
230 le32_to_cpu(caps->data[i].permitted); 230 CAP_FOR_EACH_U32(i) {
231 bprm->cap_inheritable.cap[i] = 231 __u32 value_cpu;
232 le32_to_cpu(caps->data[i].inheritable); 232
233 } 233 if (i >= tocopy) {
234 while (i < VFS_CAP_U32) { 234 /*
235 bprm->cap_permitted.cap[i] = 0; 235 * Legacy capability sets have no upper bits
236 bprm->cap_inheritable.cap[i] = 0; 236 */
237 i++; 237 bprm->cap_post_exec_permitted.cap[i] = 0;
238 continue;
239 }
240 /*
241 * pP' = (X & fP) | (pI & fI)
242 */
243 value_cpu = le32_to_cpu(caps->data[i].permitted);
244 bprm->cap_post_exec_permitted.cap[i] =
245 (current->cap_bset.cap[i] & value_cpu) |
246 (current->cap_inheritable.cap[i] &
247 le32_to_cpu(caps->data[i].inheritable));
248 if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) {
249 /*
250 * insufficient to execute correctly
251 */
252 ret = -EPERM;
253 }
238 } 254 }
239 255
240 return 0; 256 /*
257 * For legacy apps, with no internal support for recognizing they
258 * do not have enough capabilities, we return an error if they are
259 * missing some "forced" (aka file-permitted) capabilities.
260 */
261 return bprm->cap_effective ? ret : 0;
241} 262}
242 263
243/* Locate any VFS capabilities: */ 264/* Locate any VFS capabilities: */
@@ -269,9 +290,9 @@ static int get_file_caps(struct linux_binprm *bprm)
269 goto out; 290 goto out;
270 291
271 rc = cap_from_disk(&vcaps, bprm, rc); 292 rc = cap_from_disk(&vcaps, bprm, rc);
272 if (rc) 293 if (rc == -EINVAL)
273 printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", 294 printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
274 __func__, rc, bprm->filename); 295 __func__, rc, bprm->filename);
275 296
276out: 297out:
277 dput(dentry); 298 dput(dentry);
@@ -304,25 +325,24 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
304 int ret; 325 int ret;
305 326
306 ret = get_file_caps(bprm); 327 ret = get_file_caps(bprm);
307 if (ret)
308 printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n",
309 __func__, ret, bprm->filename);
310
311 /* To support inheritance of root-permissions and suid-root
312 * executables under compatibility mode, we raise all three
313 * capability sets for the file.
314 *
315 * If only the real uid is 0, we only raise the inheritable
316 * and permitted sets of the executable file.
317 */
318 328
319 if (!issecure (SECURE_NOROOT)) { 329 if (!issecure(SECURE_NOROOT)) {
330 /*
331 * To support inheritance of root-permissions and suid-root
332 * executables under compatibility mode, we override the
333 * capability sets for the file.
334 *
335 * If only the real uid is 0, we do not set the effective
336 * bit.
337 */
320 if (bprm->e_uid == 0 || current->uid == 0) { 338 if (bprm->e_uid == 0 || current->uid == 0) {
321 cap_set_full (bprm->cap_inheritable); 339 /* pP' = (cap_bset & ~0) | (pI & ~0) */
322 cap_set_full (bprm->cap_permitted); 340 bprm->cap_post_exec_permitted = cap_combine(
341 current->cap_bset, current->cap_inheritable
342 );
343 bprm->cap_effective = (bprm->e_uid == 0);
344 ret = 0;
323 } 345 }
324 if (bprm->e_uid == 0)
325 bprm->cap_effective = true;
326 } 346 }
327 347
328 return ret; 348 return ret;
@@ -330,17 +350,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
330 350
331void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) 351void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
332{ 352{
333 /* Derived from fs/exec.c:compute_creds. */
334 kernel_cap_t new_permitted, working;
335
336 new_permitted = cap_intersect(bprm->cap_permitted,
337 current->cap_bset);
338 working = cap_intersect(bprm->cap_inheritable,
339 current->cap_inheritable);
340 new_permitted = cap_combine(new_permitted, working);
341
342 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || 353 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
343 !cap_issubset (new_permitted, current->cap_permitted)) { 354 !cap_issubset(bprm->cap_post_exec_permitted,
355 current->cap_permitted)) {
344 set_dumpable(current->mm, suid_dumpable); 356 set_dumpable(current->mm, suid_dumpable);
345 current->pdeath_signal = 0; 357 current->pdeath_signal = 0;
346 358
@@ -350,9 +362,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
350 bprm->e_gid = current->gid; 362 bprm->e_gid = current->gid;
351 } 363 }
352 if (cap_limit_ptraced_target()) { 364 if (cap_limit_ptraced_target()) {
353 new_permitted = 365 bprm->cap_post_exec_permitted = cap_intersect(
354 cap_intersect(new_permitted, 366 bprm->cap_post_exec_permitted,
355 current->cap_permitted); 367 current->cap_permitted);
356 } 368 }
357 } 369 }
358 } 370 }
@@ -364,9 +376,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
364 * in the init_task struct. Thus we skip the usual 376 * in the init_task struct. Thus we skip the usual
365 * capability rules */ 377 * capability rules */
366 if (!is_global_init(current)) { 378 if (!is_global_init(current)) {
367 current->cap_permitted = new_permitted; 379 current->cap_permitted = bprm->cap_post_exec_permitted;
368 if (bprm->cap_effective) 380 if (bprm->cap_effective)
369 current->cap_effective = new_permitted; 381 current->cap_effective = bprm->cap_post_exec_permitted;
370 else 382 else
371 cap_clear(current->cap_effective); 383 cap_clear(current->cap_effective);
372 } 384 }
@@ -381,9 +393,7 @@ int cap_bprm_secureexec (struct linux_binprm *bprm)
381 if (current->uid != 0) { 393 if (current->uid != 0) {
382 if (bprm->cap_effective) 394 if (bprm->cap_effective)
383 return 1; 395 return 1;
384 if (!cap_isclear(bprm->cap_permitted)) 396 if (!cap_isclear(bprm->cap_post_exec_permitted))
385 return 1;
386 if (!cap_isclear(bprm->cap_inheritable))
387 return 1; 397 return 1;
388 } 398 }
389 399