Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

exec: Ensure mm->user_ns contains the execed files

When the user namespace support was merged the need to prevent
ptrace from revealing the contents of an unreadable executable
was overlooked.

Correct this oversight by ensuring that the executed file
or files are in mm->user_ns, by adjusting mm->user_ns.

Use the new function privileged_wrt_inode_uidgid to see if
the executable is a member of the user namespace, and as such
if having CAP_SYS_PTRACE in the user namespace should allow
tracing the executable. If not update mm->user_ns to
the parent user namespace until an appropriate parent is found.

Cc: stable@vger.kernel.org
Reported-by: Jann Horn <jann@thejh.net>
Fixes: 9e4a36ece652 ("userns: Fail exec for suid and sgid binaries with ids outside our user namespace.")
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>

+32 -4
+17 -2
fs/exec.c
··· 1275 1275 1276 1276 void would_dump(struct linux_binprm *bprm, struct file *file) 1277 1277 { 1278 - if (inode_permission(file_inode(file), MAY_READ) < 0) 1278 + struct inode *inode = file_inode(file); 1279 + if (inode_permission(inode, MAY_READ) < 0) { 1280 + struct user_namespace *old, *user_ns; 1279 1281 bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; 1282 + 1283 + /* Ensure mm->user_ns contains the executable */ 1284 + user_ns = old = bprm->mm->user_ns; 1285 + while ((user_ns != &init_user_ns) && 1286 + !privileged_wrt_inode_uidgid(user_ns, inode)) 1287 + user_ns = user_ns->parent; 1288 + 1289 + if (old != user_ns) { 1290 + bprm->mm->user_ns = get_user_ns(user_ns); 1291 + put_user_ns(old); 1292 + } 1293 + } 1280 1294 } 1281 1295 EXPORT_SYMBOL(would_dump); 1282 1296 ··· 1320 1306 !gid_eq(bprm->cred->gid, current_egid())) { 1321 1307 current->pdeath_signal = 0; 1322 1308 } else { 1323 - would_dump(bprm, bprm->file); 1324 1309 if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) 1325 1310 set_dumpable(current->mm, suid_dumpable); 1326 1311 } ··· 1753 1740 retval = copy_strings(bprm->argc, argv, bprm); 1754 1741 if (retval < 0) 1755 1742 goto out; 1743 + 1744 + would_dump(bprm, bprm->file); 1756 1745 1757 1746 retval = exec_binprm(bprm); 1758 1747 if (retval < 0)
+1
include/linux/capability.h
··· 240 240 return true; 241 241 } 242 242 #endif /* CONFIG_MULTIUSER */ 243 + extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode); 243 244 extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); 244 245 extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); 245 246 extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
+14 -2
kernel/capability.c
··· 457 457 EXPORT_SYMBOL(file_ns_capable); 458 458 459 459 /** 460 + * privileged_wrt_inode_uidgid - Do capabilities in the namespace work over the inode? 461 + * @ns: The user namespace in question 462 + * @inode: The inode in question 463 + * 464 + * Return true if the inode uid and gid are within the namespace. 465 + */ 466 + bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode) 467 + { 468 + return kuid_has_mapping(ns, inode->i_uid) && 469 + kgid_has_mapping(ns, inode->i_gid); 470 + } 471 + 472 + /** 460 473 * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped 461 474 * @inode: The inode in question 462 475 * @cap: The capability in question ··· 482 469 { 483 470 struct user_namespace *ns = current_user_ns(); 484 471 485 - return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) && 486 - kgid_has_mapping(ns, inode->i_gid); 472 + return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode); 487 473 } 488 474 EXPORT_SYMBOL(capable_wrt_inode_uidgid); 489 475