exec: Control flow simplifications

It is hard to follow the control flow in exec.c as the code has evolved over
time and something that used to work one way now works another.  This set of
changes attempts to address the worst of that, to remove unnecessary work
and to make the code a little easier to follow.

The churn is a bit higher than the last version of this patchset, with
renaming and cleaning up of comments.  I have split security_bprm_set_creds
into security_bprm_creds_for_exec and security_bprm_repopulate_creds.  My
goal was to make it clear that one hook completes its work while the other
recaculates it's work each time a new interpreter is selected.

I have added a new change at the beginning to make it clear that neither
security_bprm_creds_for_exec nor security_bprm_repopulate_creds needs to be
implemented as prepare_exec_creds properly does the work of setting up
credentials unless something special is going on.

I have made the execfd support generic and moved out of binfmt_misc so that
I can remove the recursion.

I have moved reassigning bprm->file into the loop that replaces the
recursion.  In doing so I discovered that binfmt_misc was naughty and
was returning -ENOEXEC in such a way that the search_binary_handler loop
could not continue.  So I added a change to remove that naughtiness.

Eric W. Biederman (8):
      exec: Teach prepare_exec_creds how exec treats uids & gids
      exec: Factor security_bprm_creds_for_exec out of security_bprm_set_creds
      exec: Convert security_bprm_set_creds into security_bprm_repopulate_creds
      exec: Allow load_misc_binary to call prepare_binfmt unconditionally
      exec: Move the call of prepare_binprm into search_binary_handler
      exec/binfmt_script: Don't modify bprm->buf and then return -ENOEXEC
      exec: Generic execfd support
      exec: Remove recursion from search_binary_handler

 arch/alpha/kernel/binfmt_loader.c  | 11 +----
 fs/binfmt_elf.c                    |  4 +-
 fs/binfmt_elf_fdpic.c              |  4 +-
 fs/binfmt_em86.c                   | 13 +----
 fs/binfmt_misc.c                   | 69 ++++-----------------------
 fs/binfmt_script.c                 | 82 ++++++++++++++------------------
 fs/exec.c                          | 97 ++++++++++++++++++++++++++------------
 include/linux/binfmts.h            | 36 ++++++--------
 include/linux/lsm_hook_defs.h      |  3 +-
 include/linux/lsm_hooks.h          | 52 +++++++++++---------
 include/linux/security.h           | 14 ++++--
 kernel/cred.c                      |  3 ++
 security/apparmor/domain.c         |  7 +--
 security/apparmor/include/domain.h |  2 +-
 security/apparmor/lsm.c            |  2 +-
 security/commoncap.c               |  9 ++--
 security/security.c                |  9 +++-
 security/selinux/hooks.c           |  8 ++--
 security/smack/smack_lsm.c         |  9 ++--
 security/tomoyo/tomoyo.c           | 12 ++---
 20 files changed, 202 insertions(+), 244 deletions(-)

Link: https://lkml.kernel.org/r/877dx822er.fsf_-_@x220.int.ebiederm.org
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
This commit is contained in:
Eric W. Biederman 2020-05-21 10:23:52 -05:00
commit 9d9488d4d7
20 changed files with 202 additions and 244 deletions

View file

@ -19,10 +19,6 @@ static int load_binary(struct linux_binprm *bprm)
if (bprm->loader)
return -ENOEXEC;
allow_write_access(bprm->file);
fput(bprm->file);
bprm->file = NULL;
loader = bprm->vma->vm_end - sizeof(void *);
file = open_exec("/sbin/loader");
@ -33,12 +29,9 @@ static int load_binary(struct linux_binprm *bprm)
/* Remember if the application is TASO. */
bprm->taso = eh->ah.entry < 0x100000000UL;
bprm->file = file;
bprm->interpreter = file;
bprm->loader = loader;
retval = prepare_binprm(bprm);
if (retval < 0)
return retval;
return search_binary_handler(bprm);
return 0;
}
static struct linux_binfmt loader_format = {

View file

@ -273,8 +273,8 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
NEW_AUX_ENT(AT_BASE_PLATFORM,
(elf_addr_t)(unsigned long)u_base_platform);
}
if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
if (bprm->have_execfd) {
NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
}
#undef NEW_AUX_ENT
/* AT_NULL is zero; clear the rest too */

View file

@ -628,10 +628,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
(elf_addr_t) (unsigned long) u_base_platform);
}
if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
if (bprm->have_execfd) {
nr = 0;
csp -= 2 * sizeof(unsigned long);
NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
}
nr = 0;

View file

@ -48,10 +48,6 @@ static int load_em86(struct linux_binprm *bprm)
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
return -ENOENT;
allow_write_access(bprm->file);
fput(bprm->file);
bprm->file = NULL;
/* Unlike in the script case, we don't have to do any hairy
* parsing to find our interpreter... it's hardcoded!
*/
@ -89,13 +85,8 @@ static int load_em86(struct linux_binprm *bprm)
if (IS_ERR(file))
return PTR_ERR(file);
bprm->file = file;
retval = prepare_binprm(bprm);
if (retval < 0)
return retval;
return search_binary_handler(bprm);
bprm->interpreter = file;
return 0;
}
static struct linux_binfmt em86_format = {

View file

@ -134,7 +134,6 @@ static int load_misc_binary(struct linux_binprm *bprm)
Node *fmt;
struct file *interp_file = NULL;
int retval;
int fd_binary = -1;
retval = -ENOEXEC;
if (!enabled)
@ -160,51 +159,25 @@ static int load_misc_binary(struct linux_binprm *bprm)
goto ret;
}
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
if (fmt->flags & MISC_FMT_OPEN_BINARY)
bprm->have_execfd = 1;
/* if the binary should be opened on behalf of the
* interpreter than keep it open and assign descriptor
* to it
*/
fd_binary = get_unused_fd_flags(0);
if (fd_binary < 0) {
retval = fd_binary;
goto ret;
}
fd_install(fd_binary, bprm->file);
/* if the binary is not readable than enforce mm->dumpable=0
regardless of the interpreter's permissions */
would_dump(bprm, bprm->file);
allow_write_access(bprm->file);
bprm->file = NULL;
/* mark the bprm that fd should be passed to interp */
bprm->interp_flags |= BINPRM_FLAGS_EXECFD;
bprm->interp_data = fd_binary;
} else {
allow_write_access(bprm->file);
fput(bprm->file);
bprm->file = NULL;
}
/* make argv[1] be the path to the binary */
retval = copy_strings_kernel(1, &bprm->interp, bprm);
if (retval < 0)
goto error;
goto ret;
bprm->argc++;
/* add the interp as argv[0] */
retval = copy_strings_kernel(1, &fmt->interpreter, bprm);
if (retval < 0)
goto error;
goto ret;
bprm->argc++;
/* Update interp in case binfmt_script needs it. */
retval = bprm_change_interp(fmt->interpreter, bprm);
if (retval < 0)
goto error;
goto ret;
if (fmt->flags & MISC_FMT_OPEN_FILE) {
interp_file = file_clone_open(fmt->interp_file);
@ -215,38 +188,16 @@ static int load_misc_binary(struct linux_binprm *bprm)
}
retval = PTR_ERR(interp_file);
if (IS_ERR(interp_file))
goto error;
goto ret;
bprm->file = interp_file;
if (fmt->flags & MISC_FMT_CREDENTIALS) {
loff_t pos = 0;
/*
* No need to call prepare_binprm(), it's already been
* done. bprm->buf is stale, update from interp_file.
*/
memset(bprm->buf, 0, BINPRM_BUF_SIZE);
retval = kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE,
&pos);
} else
retval = prepare_binprm(bprm);
if (retval < 0)
goto error;
retval = search_binary_handler(bprm);
if (retval < 0)
goto error;
bprm->interpreter = interp_file;
if (fmt->flags & MISC_FMT_CREDENTIALS)
bprm->preserve_creds = 1;
retval = 0;
ret:
dput(fmt->dentry);
return retval;
error:
if (fd_binary > 0)
ksys_close(fd_binary);
bprm->interp_flags = 0;
bprm->interp_data = 0;
goto ret;
}
/* Command parsers */

View file

@ -16,14 +16,14 @@
#include <linux/fs.h>
static inline bool spacetab(char c) { return c == ' ' || c == '\t'; }
static inline char *next_non_spacetab(char *first, const char *last)
static inline const char *next_non_spacetab(const char *first, const char *last)
{
for (; first <= last; first++)
if (!spacetab(*first))
return first;
return NULL;
}
static inline char *next_terminator(char *first, const char *last)
static inline const char *next_terminator(const char *first, const char *last)
{
for (; first <= last; first++)
if (spacetab(*first) || !*first)
@ -33,8 +33,7 @@ static inline char *next_terminator(char *first, const char *last)
static int load_script(struct linux_binprm *bprm)
{
const char *i_arg, *i_name;
char *cp, *buf_end;
const char *i_name, *i_sep, *i_arg, *i_end, *buf_end;
struct file *file;
int retval;
@ -42,20 +41,6 @@ static int load_script(struct linux_binprm *bprm)
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
return -ENOEXEC;
/*
* If the script filename will be inaccessible after exec, typically
* because it is a "/dev/fd/<fd>/.." path against an O_CLOEXEC fd, give
* up now (on the assumption that the interpreter will want to load
* this file).
*/
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
return -ENOENT;
/* Release since we are not mapping a binary into memory. */
allow_write_access(bprm->file);
fput(bprm->file);
bprm->file = NULL;
/*
* This section handles parsing the #! line into separate
* interpreter path and argument strings. We must be careful
@ -71,39 +56,43 @@ static int load_script(struct linux_binprm *bprm)
* parse them on its own.
*/
buf_end = bprm->buf + sizeof(bprm->buf) - 1;
cp = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
if (!cp) {
cp = next_non_spacetab(bprm->buf + 2, buf_end);
if (!cp)
i_end = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
if (!i_end) {
i_end = next_non_spacetab(bprm->buf + 2, buf_end);
if (!i_end)
return -ENOEXEC; /* Entire buf is spaces/tabs */
/*
* If there is no later space/tab/NUL we must assume the
* interpreter path is truncated.
*/
if (!next_terminator(cp, buf_end))
if (!next_terminator(i_end, buf_end))
return -ENOEXEC;
cp = buf_end;
i_end = buf_end;
}
/* NUL-terminate the buffer and any trailing spaces/tabs. */
*cp = '\0';
while (cp > bprm->buf) {
cp--;
if ((*cp == ' ') || (*cp == '\t'))
*cp = '\0';
else
break;
}
for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
if (*cp == '\0')
/* Trim any trailing spaces/tabs from i_end */
while (spacetab(i_end[-1]))
i_end--;
/* Skip over leading spaces/tabs */
i_name = next_non_spacetab(bprm->buf+2, i_end);
if (!i_name || (i_name == i_end))
return -ENOEXEC; /* No interpreter name found */
i_name = cp;
/* Is there an optional argument? */
i_arg = NULL;
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
/* nothing */ ;
while ((*cp == ' ') || (*cp == '\t'))
*cp++ = '\0';
if (*cp)
i_arg = cp;
i_sep = next_terminator(i_name, i_end);
if (i_sep && (*i_sep != '\0'))
i_arg = next_non_spacetab(i_sep, i_end);
/*
* If the script filename will be inaccessible after exec, typically
* because it is a "/dev/fd/<fd>/.." path against an O_CLOEXEC fd, give
* up now (on the assumption that the interpreter will want to load
* this file).
*/
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
return -ENOENT;
/*
* OK, we've parsed out the interpreter name and
* (optional) argument.
@ -121,7 +110,9 @@ static int load_script(struct linux_binprm *bprm)
if (retval < 0)
return retval;
bprm->argc++;
*((char *)i_end) = '\0';
if (i_arg) {
*((char *)i_sep) = '\0';
retval = copy_strings_kernel(1, &i_arg, bprm);
if (retval < 0)
return retval;
@ -142,11 +133,8 @@ static int load_script(struct linux_binprm *bprm)
if (IS_ERR(file))
return PTR_ERR(file);
bprm->file = file;
retval = prepare_binprm(bprm);
if (retval < 0)
return retval;
return search_binary_handler(bprm);
bprm->interpreter = file;
return 0;
}
static struct linux_binfmt script_format = {

View file

@ -1323,7 +1323,10 @@ int begin_new_exec(struct linux_binprm * bprm)
*/
set_mm_exe_file(bprm->mm, bprm->file);
/* If the binary is not readable then enforce mm->dumpable=0 */
would_dump(bprm, bprm->file);
if (bprm->have_execfd)
would_dump(bprm, bprm->executable);
/*
* Release all of the old mmap stuff
@ -1366,7 +1369,7 @@ int begin_new_exec(struct linux_binprm * bprm)
* the final state of setuid/setgid/fscaps can be merged into the
* secureexec flag.
*/
bprm->secureexec |= bprm->cap_elevated;
bprm->secureexec |= bprm->active_secureexec;
if (bprm->secureexec) {
/* Make sure parent cannot signal privileged process. */
@ -1427,6 +1430,16 @@ int begin_new_exec(struct linux_binprm * bprm)
* credentials; any time after this it may be unlocked.
*/
security_bprm_committed_creds(bprm);
/* Pass the opened binary to the interpreter. */
if (bprm->have_execfd) {
retval = get_unused_fd_flags(0);
if (retval < 0)
goto out_unlock;
fd_install(retval, bprm->executable);
bprm->executable = NULL;
bprm->execfd = retval;
}
return 0;
out_unlock:
@ -1516,6 +1529,8 @@ static void free_bprm(struct linux_binprm *bprm)
allow_write_access(bprm->file);
fput(bprm->file);
}
if (bprm->executable)
fput(bprm->executable);
/* If a binfmt changed the interp, free it. */
if (bprm->interp != bprm->filename)
kfree(bprm->interp);
@ -1629,25 +1644,27 @@ static void bprm_fill_uid(struct linux_binprm *bprm)
*
* This may be called multiple times for binary chains (scripts for example).
*/
int prepare_binprm(struct linux_binprm *bprm)
static int prepare_binprm(struct linux_binprm *bprm)
{
int retval;
loff_t pos = 0;
bprm_fill_uid(bprm);
/* Can the interpreter get to the executable without races? */
if (!bprm->preserve_creds) {
int retval;
/* fill in binprm security blob */
retval = security_bprm_set_creds(bprm);
if (retval)
return retval;
bprm->called_set_creds = 1;
/* Recompute parts of bprm->cred based on bprm->file */
bprm->active_secureexec = 0;
bprm_fill_uid(bprm);
retval = security_bprm_repopulate_creds(bprm);
if (retval)
return retval;
}
bprm->preserve_creds = 0;
memset(bprm->buf, 0, BINPRM_BUF_SIZE);
return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
}
EXPORT_SYMBOL(prepare_binprm);
/*
* Arguments are '\0' separated strings found at the location bprm->p
* points to; chop off the first by relocating brpm->p to right after
@ -1693,15 +1710,15 @@ EXPORT_SYMBOL(remove_arg_zero);
/*
* cycle the list of binary formats handler, until one recognizes the image
*/
int search_binary_handler(struct linux_binprm *bprm)
static int search_binary_handler(struct linux_binprm *bprm)
{
bool need_retry = IS_ENABLED(CONFIG_MODULES);
struct linux_binfmt *fmt;
int retval;
/* This allows 4 levels of binfmt rewrites before failing hard. */
if (bprm->recursion_depth > 5)
return -ELOOP;
retval = prepare_binprm(bprm);
if (retval < 0)
return retval;
retval = security_bprm_check(bprm);
if (retval)
@ -1715,14 +1732,11 @@ int search_binary_handler(struct linux_binprm *bprm)
continue;
read_unlock(&binfmt_lock);
bprm->recursion_depth++;
retval = fmt->load_binary(bprm);
bprm->recursion_depth--;
read_lock(&binfmt_lock);
put_binfmt(fmt);
if (bprm->point_of_no_return || !bprm->file ||
(retval != -ENOEXEC)) {
if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
read_unlock(&binfmt_lock);
return retval;
}
@ -1741,12 +1755,11 @@ int search_binary_handler(struct linux_binprm *bprm)
return retval;
}
EXPORT_SYMBOL(search_binary_handler);
static int exec_binprm(struct linux_binprm *bprm)
{
pid_t old_pid, old_vpid;
int ret;
int ret, depth;
/* Need to fetch pid before load_binary changes it */
old_pid = current->pid;
@ -1754,15 +1767,38 @@ static int exec_binprm(struct linux_binprm *bprm)
old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
rcu_read_unlock();
ret = search_binary_handler(bprm);
if (ret >= 0) {
audit_bprm(bprm);
trace_sched_process_exec(current, old_pid, bprm);
ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
proc_exec_connector(current);
/* This allows 4 levels of binfmt rewrites before failing hard. */
for (depth = 0;; depth++) {
struct file *exec;
if (depth > 5)
return -ELOOP;
ret = search_binary_handler(bprm);
if (ret < 0)
return ret;
if (!bprm->interpreter)
break;
exec = bprm->file;
bprm->file = bprm->interpreter;
bprm->interpreter = NULL;
allow_write_access(exec);
if (unlikely(bprm->have_execfd)) {
if (bprm->executable) {
fput(exec);
return -ENOEXEC;
}
bprm->executable = exec;
} else
fput(exec);
}
return ret;
audit_bprm(bprm);
trace_sched_process_exec(current, old_pid, bprm);
ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
proc_exec_connector(current);
return 0;
}
/*
@ -1855,8 +1891,9 @@ static int __do_execve_file(int fd, struct filename *filename,
if (retval < 0)
goto out;
retval = prepare_binprm(bprm);
if (retval < 0)
/* Set the unchanging part of bprm->cred */
retval = security_bprm_creds_for_exec(bprm);
if (retval)
goto out;
retval = copy_strings_kernel(1, &bprm->filename, bprm);

View file

@ -26,23 +26,20 @@ struct linux_binprm {
unsigned long p; /* current top of mem */
unsigned long argmin; /* rlimit marker for copy_strings() */
unsigned int
/* Should an execfd be passed to userspace? */
have_execfd:1,
/* It is safe to use the creds of a script (see binfmt_misc) */
preserve_creds:1,
/*
* True after the bprm_set_creds hook has been called once
* (multiple calls can be made via prepare_binprm() for
* binfmt_script/misc).
* True if most recent call to security_bprm_set_creds
* resulted in elevated privileges.
*/
called_set_creds:1,
active_secureexec:1,
/*
* True if most recent call to the commoncaps bprm_set_creds
* hook (due to multiple prepare_binprm() calls from the
* binfmt_script/misc handlers) resulted in elevated
* privileges.
*/
cap_elevated:1,
/*
* Set by bprm_set_creds hook to indicate a privilege-gaining
* exec has happened. Used to sanitize execution environment
* and to set AT_SECURE auxv for glibc.
* Set by bprm_creds_for_exec hook to indicate a
* privilege-gaining exec has happened. Used to set
* AT_SECURE auxv for glibc.
*/
secureexec:1,
/*
@ -53,7 +50,8 @@ struct linux_binprm {
#ifdef __alpha__
unsigned int taso:1;
#endif
unsigned int recursion_depth; /* only for search_binary_handler() */
struct file * executable; /* Executable to pass to the interpreter */
struct file * interpreter;
struct file * file;
struct cred *cred; /* new credentials */
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
@ -64,7 +62,7 @@ struct linux_binprm {
of the time same as filename, but could be
different for binfmt_{misc,script} */
unsigned interp_flags;
unsigned interp_data;
int execfd; /* File descriptor of the executable */
unsigned long loader, exec;
struct rlimit rlim_stack; /* Saved RLIMIT_STACK used during exec. */
@ -75,10 +73,6 @@ struct linux_binprm {
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
/* fd of the binary should be passed to the interpreter */
#define BINPRM_FLAGS_EXECFD_BIT 1
#define BINPRM_FLAGS_EXECFD (1 << BINPRM_FLAGS_EXECFD_BIT)
/* filename of the binary will be inaccessible after exec */
#define BINPRM_FLAGS_PATH_INACCESSIBLE_BIT 2
#define BINPRM_FLAGS_PATH_INACCESSIBLE (1 << BINPRM_FLAGS_PATH_INACCESSIBLE_BIT)
@ -122,9 +116,7 @@ static inline void insert_binfmt(struct linux_binfmt *fmt)
extern void unregister_binfmt(struct linux_binfmt *);
extern int prepare_binprm(struct linux_binprm *);
extern int __must_check remove_arg_zero(struct linux_binprm *);
extern int search_binary_handler(struct linux_binprm *);
extern int begin_new_exec(struct linux_binprm * bprm);
extern void setup_new_exec(struct linux_binprm * bprm);
extern void finalize_exec(struct linux_binprm *bprm);

View file

@ -49,7 +49,8 @@ LSM_HOOK(int, 0, syslog, int type)
LSM_HOOK(int, 0, settime, const struct timespec64 *ts,
const struct timezone *tz)
LSM_HOOK(int, 0, vm_enough_memory, struct mm_struct *mm, long pages)
LSM_HOOK(int, 0, bprm_set_creds, struct linux_binprm *bprm)
LSM_HOOK(int, 0, bprm_creds_for_exec, struct linux_binprm *bprm)
LSM_HOOK(int, 0, bprm_repopulate_creds, struct linux_binprm *bprm)
LSM_HOOK(int, 0, bprm_check_security, struct linux_binprm *bprm)
LSM_HOOK(void, LSM_RET_VOID, bprm_committing_creds, struct linux_binprm *bprm)
LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, struct linux_binprm *bprm)

View file

@ -34,40 +34,46 @@
*
* Security hooks for program execution operations.
*
* @bprm_set_creds:
* Save security information in the bprm->security field, typically based
* on information about the bprm->file, for later use by the apply_creds
* hook. This hook may also optionally check permissions (e.g. for
* @bprm_creds_for_exec:
* If the setup in prepare_exec_creds did not setup @bprm->cred->security
* properly for executing @bprm->file, update the LSM's portion of
* @bprm->cred->security to be what commit_creds needs to install for the
* new program. This hook may also optionally check permissions
* (e.g. for transitions between security domains).
* The hook must set @bprm->secureexec to 1 if AT_SECURE should be set to
* request libc enable secure mode.
* @bprm contains the linux_binprm structure.
* Return 0 if the hook is successful and permission is granted.
* @bprm_repopulate_creds:
* Assuming that the relevant bits of @bprm->cred->security have been
* previously set, examine @bprm->file and regenerate them. This is
* so that the credentials derived from the interpreter the code is
* actually going to run are used rather than credentials derived
* from a script. This done because the interpreter binary needs to
* reopen script, and may end up opening something completely different.
* This hook may also optionally check permissions (e.g. for
* transitions between security domains).
* This hook may be called multiple times during a single execve, e.g. for
* interpreters. The hook can tell whether it has already been called by
* checking to see if @bprm->security is non-NULL. If so, then the hook
* may decide either to retain the security information saved earlier or
* to replace it. The hook must set @bprm->secureexec to 1 if a "secure
* exec" has happened as a result of this hook call. The flag is used to
* indicate the need for a sanitized execution environment, and is also
* passed in the ELF auxiliary table on the initial stack to indicate
* whether libc should enable secure mode.
* The hook must set @bprm->active_secureexec to 1 if AT_SECURE should be set to
* request libc enable secure mode.
* @bprm contains the linux_binprm structure.
* Return 0 if the hook is successful and permission is granted.
* @bprm_check_security:
* This hook mediates the point when a search for a binary handler will
* begin. It allows a check the @bprm->security value which is set in the
* preceding set_creds call. The primary difference from set_creds is
* that the argv list and envp list are reliably available in @bprm. This
* hook may be called multiple times during a single execve; and in each
* pass set_creds is called first.
* begin. It allows a check against the @bprm->cred->security value
* which was set in the preceding creds_for_exec call. The argv list and
* envp list are reliably available in @bprm. This hook may be called
* multiple times during a single execve.
* @bprm contains the linux_binprm structure.
* Return 0 if the hook is successful and permission is granted.
* @bprm_committing_creds:
* Prepare to install the new security attributes of a process being
* transformed by an execve operation, based on the old credentials
* pointed to by @current->cred and the information set in @bprm->cred by
* the bprm_set_creds hook. @bprm points to the linux_binprm structure.
* This hook is a good place to perform state changes on the process such
* as closing open file descriptors to which access will no longer be
* granted when the attributes are changed. This is called immediately
* before commit_creds().
* the bprm_creds_for_exec hook. @bprm points to the linux_binprm
* structure. This hook is a good place to perform state changes on the
* process such as closing open file descriptors to which access will no
* longer be granted when the attributes are changed. This is called
* immediately before commit_creds().
* @bprm_committed_creds:
* Tidy up after the installation of the new security attributes of a
* process being transformed by an execve operation. The new credentials

View file

@ -140,7 +140,7 @@ extern int cap_capset(struct cred *new, const struct cred *old,
const kernel_cap_t *effective,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted);
extern int cap_bprm_set_creds(struct linux_binprm *bprm);
extern int cap_bprm_repopulate_creds(struct linux_binprm *bprm);
extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags);
extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
@ -276,7 +276,8 @@ int security_quota_on(struct dentry *dentry);
int security_syslog(int type);
int security_settime64(const struct timespec64 *ts, const struct timezone *tz);
int security_vm_enough_memory_mm(struct mm_struct *mm, long pages);
int security_bprm_set_creds(struct linux_binprm *bprm);
int security_bprm_creds_for_exec(struct linux_binprm *bprm);
int security_bprm_repopulate_creds(struct linux_binprm *bprm);
int security_bprm_check(struct linux_binprm *bprm);
void security_bprm_committing_creds(struct linux_binprm *bprm);
void security_bprm_committed_creds(struct linux_binprm *bprm);
@ -569,9 +570,14 @@ static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
return __vm_enough_memory(mm, pages, cap_vm_enough_memory(mm, pages));
}
static inline int security_bprm_set_creds(struct linux_binprm *bprm)
static inline int security_bprm_creds_for_exec(struct linux_binprm *bprm)
{
return cap_bprm_set_creds(bprm);
return 0;
}
static inline int security_bprm_repopulate_creds(struct linux_binprm *bprm)
{
return cap_bprm_repopulate_creds(bprm);
}
static inline int security_bprm_check(struct linux_binprm *bprm)

View file

@ -315,6 +315,9 @@ struct cred *prepare_exec_creds(void)
new->process_keyring = NULL;
#endif
new->suid = new->fsuid = new->euid;
new->sgid = new->fsgid = new->egid;
return new;
}

View file

@ -854,14 +854,14 @@ static struct aa_label *handle_onexec(struct aa_label *label,
}
/**
* apparmor_bprm_set_creds - set the new creds on the bprm struct
* apparmor_bprm_creds_for_exec - Update the new creds on the bprm struct
* @bprm: binprm for the exec (NOT NULL)
*
* Returns: %0 or error on failure
*
* TODO: once the other paths are done see if we can't refactor into a fn
*/
int apparmor_bprm_set_creds(struct linux_binprm *bprm)
int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
{
struct aa_task_ctx *ctx;
struct aa_label *label, *new = NULL;
@ -875,9 +875,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
file_inode(bprm->file)->i_mode
};
if (bprm->called_set_creds)
return 0;
ctx = task_ctx(current);
AA_BUG(!cred_label(bprm->cred));
AA_BUG(!ctx);

View file

@ -30,7 +30,7 @@ struct aa_domain {
struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
const char **name);
int apparmor_bprm_set_creds(struct linux_binprm *bprm);
int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm);
void aa_free_domain_entries(struct aa_domain *domain);
int aa_change_hat(const char *hats[], int count, u64 token, int flags);

View file

@ -1232,7 +1232,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
LSM_HOOK_INIT(cred_transfer, apparmor_cred_transfer),
LSM_HOOK_INIT(bprm_set_creds, apparmor_bprm_set_creds),
LSM_HOOK_INIT(bprm_creds_for_exec, apparmor_bprm_creds_for_exec),
LSM_HOOK_INIT(bprm_committing_creds, apparmor_bprm_committing_creds),
LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds),

View file

@ -797,14 +797,14 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old,
}
/**
* cap_bprm_set_creds - Set up the proposed credentials for execve().
* cap_bprm_repopulate_creds - Set up the proposed credentials for execve().
* @bprm: The execution parameters, including the proposed creds
*
* Set up the proposed credentials for a new execution context being
* constructed by execve(). The proposed creds in @bprm->cred is altered,
* which won't take effect immediately. Returns 0 if successful, -ve on error.
*/
int cap_bprm_set_creds(struct linux_binprm *bprm)
int cap_bprm_repopulate_creds(struct linux_binprm *bprm)
{
const struct cred *old = current_cred();
struct cred *new = bprm->cred;
@ -884,12 +884,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
return -EPERM;
/* Check for privilege-elevated exec. */
bprm->cap_elevated = 0;
if (is_setid ||
(!__is_real(root_uid, new) &&
(effective ||
__cap_grew(permitted, ambient, new))))
bprm->cap_elevated = 1;
bprm->active_secureexec = 1;
return 0;
}
@ -1346,7 +1345,7 @@ static struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme),
LSM_HOOK_INIT(capget, cap_capget),
LSM_HOOK_INIT(capset, cap_capset),
LSM_HOOK_INIT(bprm_set_creds, cap_bprm_set_creds),
LSM_HOOK_INIT(bprm_repopulate_creds, cap_bprm_repopulate_creds),
LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity),

View file

@ -823,9 +823,14 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
return __vm_enough_memory(mm, pages, cap_sys_admin);
}
int security_bprm_set_creds(struct linux_binprm *bprm)
int security_bprm_creds_for_exec(struct linux_binprm *bprm)
{
return call_int_hook(bprm_set_creds, 0, bprm);
return call_int_hook(bprm_creds_for_exec, 0, bprm);
}
int security_bprm_repopulate_creds(struct linux_binprm *bprm)
{
return call_int_hook(bprm_repopulate_creds, 0, bprm);
}
int security_bprm_check(struct linux_binprm *bprm)

View file

@ -2286,7 +2286,7 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm,
return -EACCES;
}
static int selinux_bprm_set_creds(struct linux_binprm *bprm)
static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
{
const struct task_security_struct *old_tsec;
struct task_security_struct *new_tsec;
@ -2297,8 +2297,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
/* SELinux context only depends on initial program or script and not
* the script interpreter */
if (bprm->called_set_creds)
return 0;
old_tsec = selinux_cred(current_cred());
new_tsec = selinux_cred(bprm->cred);
@ -6385,7 +6383,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
/* Permission checking based on the specified context is
performed during the actual operation (execve,
open/mkdir/...), when we know the full context of the
operation. See selinux_bprm_set_creds for the execve
operation. See selinux_bprm_creds_for_exec for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
tsec = selinux_cred(new);
@ -6914,7 +6912,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(netlink_send, selinux_netlink_send),
LSM_HOOK_INIT(bprm_set_creds, selinux_bprm_set_creds),
LSM_HOOK_INIT(bprm_creds_for_exec, selinux_bprm_creds_for_exec),
LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),

View file

@ -891,12 +891,12 @@ static int smack_sb_statfs(struct dentry *dentry)
*/
/**
* smack_bprm_set_creds - set creds for exec
* smack_bprm_creds_for_exec - Update bprm->cred if needed for exec
* @bprm: the exec information
*
* Returns 0 if it gets a blob, -EPERM if exec forbidden and -ENOMEM otherwise
*/
static int smack_bprm_set_creds(struct linux_binprm *bprm)
static int smack_bprm_creds_for_exec(struct linux_binprm *bprm)
{
struct inode *inode = file_inode(bprm->file);
struct task_smack *bsp = smack_cred(bprm->cred);
@ -904,9 +904,6 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
struct superblock_smack *sbsp;
int rc;
if (bprm->called_set_creds)
return 0;
isp = smack_inode(inode);
if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
return 0;
@ -4598,7 +4595,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
LSM_HOOK_INIT(bprm_creds_for_exec, smack_bprm_creds_for_exec),
LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),

View file

@ -63,20 +63,14 @@ static void tomoyo_bprm_committed_creds(struct linux_binprm *bprm)
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
/**
* tomoyo_bprm_set_creds - Target for security_bprm_set_creds().
* tomoyo_bprm_for_exec - Target for security_bprm_creds_for_exec().
*
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0.
*/
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
static int tomoyo_bprm_creds_for_exec(struct linux_binprm *bprm)
{
/*
* Do only if this function is called for the first time of an execve
* operation.
*/
if (bprm->called_set_creds)
return 0;
/*
* Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
* for the first time.
@ -539,7 +533,7 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(task_alloc, tomoyo_task_alloc),
LSM_HOOK_INIT(task_free, tomoyo_task_free),
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
LSM_HOOK_INIT(bprm_set_creds, tomoyo_bprm_set_creds),
LSM_HOOK_INIT(bprm_creds_for_exec, tomoyo_bprm_creds_for_exec),
#endif
LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security),
LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl),