Skip to content

Commit

Permalink
exec: Ensure mm->user_ns contains the execed files
Browse files Browse the repository at this point in the history
commit f84df2a upstream.

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.

Reported-by: Jann Horn <jann@thejh.net>
Fixes: 9e4a36e ("userns: Fail exec for suid and sgid binaries with ids outside our user namespace.")
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
[bwh: Backported to 3.16:
 - Add #include <linux/user_namespace.h>
 - Adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
  • Loading branch information
Eric W. Biederman authored and Ben Hutchings committed Jan 1, 2018
1 parent d55a94e commit a0d8337
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 4 deletions.
20 changes: 18 additions & 2 deletions fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <linux/pipe_fs_i.h>
#include <linux/oom.h>
#include <linux/compat.h>
#include <linux/user_namespace.h>

#include <asm/uaccess.h>
#include <asm/mmu_context.h>
Expand Down Expand Up @@ -1129,8 +1130,22 @@ EXPORT_SYMBOL(flush_old_exec);

void would_dump(struct linux_binprm *bprm, struct file *file)
{
if (inode_permission(file_inode(file), MAY_READ) < 0)
struct inode *inode = file_inode(file);
if (inode_permission(inode, MAY_READ) < 0) {
struct user_namespace *old, *user_ns;
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;

/* Ensure mm->user_ns contains the executable */
user_ns = old = bprm->mm->user_ns;
while ((user_ns != &init_user_ns) &&
!privileged_wrt_inode_uidgid(user_ns, inode))
user_ns = user_ns->parent;

if (old != user_ns) {
bprm->mm->user_ns = get_user_ns(user_ns);
put_user_ns(old);
}
}
}
EXPORT_SYMBOL(would_dump);

Expand Down Expand Up @@ -1160,7 +1175,6 @@ void setup_new_exec(struct linux_binprm * bprm)
!gid_eq(bprm->cred->gid, current_egid())) {
current->pdeath_signal = 0;
} else {
would_dump(bprm, bprm->file);
if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)
set_dumpable(current->mm, suid_dumpable);
}
Expand Down Expand Up @@ -1564,6 +1578,8 @@ static int do_execve_common(struct filename *filename,
if (retval < 0)
goto out;

would_dump(bprm, bprm->file);

retval = exec_binprm(bprm);
if (retval < 0)
goto out;
Expand Down
1 change: 1 addition & 0 deletions include/linux/capability.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ extern bool has_ns_capability_noaudit(struct task_struct *t,
struct user_namespace *ns, int cap);
extern bool capable(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
Expand Down
16 changes: 14 additions & 2 deletions kernel/capability.c
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,19 @@ bool capable(int cap)
}
EXPORT_SYMBOL(capable);

/**
* privileged_wrt_inode_uidgid - Do capabilities in the namespace work over the inode?
* @ns: The user namespace in question
* @inode: The inode in question
*
* Return true if the inode uid and gid are within the namespace.
*/
bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode)
{
return kuid_has_mapping(ns, inode->i_uid) &&
kgid_has_mapping(ns, inode->i_gid);
}

/**
* capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
* @inode: The inode in question
Expand All @@ -440,8 +453,7 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
{
struct user_namespace *ns = current_user_ns();

return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) &&
kgid_has_mapping(ns, inode->i_gid);
return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode);
}
EXPORT_SYMBOL(capable_wrt_inode_uidgid);

Expand Down

0 comments on commit a0d8337

Please sign in to comment.