Skip to content

Commit 3977e28

Browse files
committed
exec: Remove the recomputation of bprm->cred
Recomputing the uids, gids, capabilities, and related flags each time a new bprm->file is set is error prone and unnecessary. This set of changes splits per_clear temporarily into two separate variables. This is the last change necessary to ensure that everything that is computed from brpm->file in bprm->cred is recomputed every time a new bprm->file is set. Then the code is refactored to compute bprm->cred from bprm->file when the final brpm->file is known, removing the need for recomputation entirely. Doing this in two steps should allow anyone who has problems later to bisect and tell if it was the semantic change or the refactoring that caused them problems. Eric W. Biederman (2): exec: Add a per bprm->file version of per_clear exec: Compute file based creds only once fs/binfmt_misc.c | 2 +- fs/exec.c | 57 ++++++++++++++++++------------------------- include/linux/binfmts.h | 9 ++----- include/linux/lsm_hook_defs.h | 2 +- include/linux/lsm_hooks.h | 22 +++++++++-------- include/linux/security.h | 9 ++++--- security/commoncap.c | 22 +++++++++-------- security/security.c | 4 +-- 8 files changed, 59 insertions(+), 68 deletions(-) Merge branch 'exec-norecompute-v2' into exec-next
2 parents e32f887 + 56305aa commit 3977e28

File tree

8 files changed

+59
-68
lines changed

8 files changed

+59
-68
lines changed

fs/binfmt_misc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ static int load_misc_binary(struct linux_binprm *bprm)
192192

193193
bprm->interpreter = interp_file;
194194
if (fmt->flags & MISC_FMT_CREDENTIALS)
195-
bprm->preserve_creds = 1;
195+
bprm->execfd_creds = 1;
196196

197197
retval = 0;
198198
ret:

fs/exec.c

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272

7373
#include <trace/events/sched.h>
7474

75+
static int bprm_creds_from_file(struct linux_binprm *bprm);
76+
7577
int suid_dumpable = 0;
7678

7779
static LIST_HEAD(formats);
@@ -1304,6 +1306,11 @@ int begin_new_exec(struct linux_binprm * bprm)
13041306
struct task_struct *me = current;
13051307
int retval;
13061308

1309+
/* Once we are committed compute the creds */
1310+
retval = bprm_creds_from_file(bprm);
1311+
if (retval)
1312+
return retval;
1313+
13071314
/*
13081315
* Ensure all future errors are fatal.
13091316
*/
@@ -1364,13 +1371,6 @@ int begin_new_exec(struct linux_binprm * bprm)
13641371
*/
13651372
do_close_on_exec(me->files);
13661373

1367-
/*
1368-
* Once here, prepare_binrpm() will not be called any more, so
1369-
* the final state of setuid/setgid/fscaps can be merged into the
1370-
* secureexec flag.
1371-
*/
1372-
bprm->secureexec |= bprm->active_secureexec;
1373-
13741374
if (bprm->secureexec) {
13751375
/* Make sure parent cannot signal privileged process. */
13761376
me->pdeath_signal = 0;
@@ -1586,29 +1586,21 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
15861586
spin_unlock(&p->fs->lock);
15871587
}
15881588

1589-
static void bprm_fill_uid(struct linux_binprm *bprm)
1589+
static void bprm_fill_uid(struct linux_binprm *bprm, struct file *file)
15901590
{
1591+
/* Handle suid and sgid on files */
15911592
struct inode *inode;
15921593
unsigned int mode;
15931594
kuid_t uid;
15941595
kgid_t gid;
15951596

1596-
/*
1597-
* Since this can be called multiple times (via prepare_binprm),
1598-
* we must clear any previous work done when setting set[ug]id
1599-
* bits from any earlier bprm->file uses (for example when run
1600-
* first for a setuid script then again for its interpreter).
1601-
*/
1602-
bprm->cred->euid = current_euid();
1603-
bprm->cred->egid = current_egid();
1604-
1605-
if (!mnt_may_suid(bprm->file->f_path.mnt))
1597+
if (!mnt_may_suid(file->f_path.mnt))
16061598
return;
16071599

16081600
if (task_no_new_privs(current))
16091601
return;
16101602

1611-
inode = bprm->file->f_path.dentry->d_inode;
1603+
inode = file->f_path.dentry->d_inode;
16121604
mode = READ_ONCE(inode->i_mode);
16131605
if (!(mode & (S_ISUID|S_ISGID)))
16141606
return;
@@ -1638,29 +1630,28 @@ static void bprm_fill_uid(struct linux_binprm *bprm)
16381630
}
16391631
}
16401632

1633+
/*
1634+
* Compute brpm->cred based upon the final binary.
1635+
*/
1636+
static int bprm_creds_from_file(struct linux_binprm *bprm)
1637+
{
1638+
/* Compute creds based on which file? */
1639+
struct file *file = bprm->execfd_creds ? bprm->executable : bprm->file;
1640+
1641+
bprm_fill_uid(bprm, file);
1642+
return security_bprm_creds_from_file(bprm, file);
1643+
}
1644+
16411645
/*
16421646
* Fill the binprm structure from the inode.
1643-
* Check permissions, then read the first BINPRM_BUF_SIZE bytes
1647+
* Read the first BINPRM_BUF_SIZE bytes
16441648
*
16451649
* This may be called multiple times for binary chains (scripts for example).
16461650
*/
16471651
static int prepare_binprm(struct linux_binprm *bprm)
16481652
{
16491653
loff_t pos = 0;
16501654

1651-
/* Can the interpreter get to the executable without races? */
1652-
if (!bprm->preserve_creds) {
1653-
int retval;
1654-
1655-
/* Recompute parts of bprm->cred based on bprm->file */
1656-
bprm->active_secureexec = 0;
1657-
bprm_fill_uid(bprm);
1658-
retval = security_bprm_repopulate_creds(bprm);
1659-
if (retval)
1660-
return retval;
1661-
}
1662-
bprm->preserve_creds = 0;
1663-
16641655
memset(bprm->buf, 0, BINPRM_BUF_SIZE);
16651656
return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
16661657
}

include/linux/binfmts.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,8 @@ struct linux_binprm {
2929
/* Should an execfd be passed to userspace? */
3030
have_execfd:1,
3131

32-
/* It is safe to use the creds of a script (see binfmt_misc) */
33-
preserve_creds:1,
34-
/*
35-
* True if most recent call to security_bprm_set_creds
36-
* resulted in elevated privileges.
37-
*/
38-
active_secureexec:1,
32+
/* Use the creds of a script (see binfmt_misc) */
33+
execfd_creds:1,
3934
/*
4035
* Set by bprm_creds_for_exec hook to indicate a
4136
* privilege-gaining exec has happened. Used to set

include/linux/lsm_hook_defs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ LSM_HOOK(int, 0, settime, const struct timespec64 *ts,
5050
const struct timezone *tz)
5151
LSM_HOOK(int, 0, vm_enough_memory, struct mm_struct *mm, long pages)
5252
LSM_HOOK(int, 0, bprm_creds_for_exec, struct linux_binprm *bprm)
53-
LSM_HOOK(int, 0, bprm_repopulate_creds, struct linux_binprm *bprm)
53+
LSM_HOOK(int, 0, bprm_creds_from_file, struct linux_binprm *bprm, struct file *file)
5454
LSM_HOOK(int, 0, bprm_check_security, struct linux_binprm *bprm)
5555
LSM_HOOK(void, LSM_RET_VOID, bprm_committing_creds, struct linux_binprm *bprm)
5656
LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, struct linux_binprm *bprm)

include/linux/lsm_hooks.h

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,19 @@
4444
* request libc enable secure mode.
4545
* @bprm contains the linux_binprm structure.
4646
* Return 0 if the hook is successful and permission is granted.
47-
* @bprm_repopulate_creds:
48-
* Assuming that the relevant bits of @bprm->cred->security have been
49-
* previously set, examine @bprm->file and regenerate them. This is
50-
* so that the credentials derived from the interpreter the code is
51-
* actually going to run are used rather than credentials derived
52-
* from a script. This done because the interpreter binary needs to
53-
* reopen script, and may end up opening something completely different.
54-
* This hook may also optionally check permissions (e.g. for
55-
* transitions between security domains).
56-
* The hook must set @bprm->active_secureexec to 1 if AT_SECURE should be set to
47+
* @bprm_creds_from_file:
48+
* If @file is setpcap, suid, sgid or otherwise marked to change
49+
* privilege upon exec, update @bprm->cred to reflect that change.
50+
* This is called after finding the binary that will be executed.
51+
* without an interpreter. This ensures that the credentials will not
52+
* be derived from a script that the binary will need to reopen, which
53+
* when reopend may end up being a completely different file. This
54+
* hook may also optionally check permissions (e.g. for transitions
55+
* between security domains).
56+
* The hook must set @bprm->secureexec to 1 if AT_SECURE should be set to
5757
* request libc enable secure mode.
58+
* The hook must add to @bprm->per_clear any personality flags that
59+
* should be cleared from current->personality.
5860
* @bprm contains the linux_binprm structure.
5961
* Return 0 if the hook is successful and permission is granted.
6062
* @bprm_check_security:

include/linux/security.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ extern int cap_capset(struct cred *new, const struct cred *old,
140140
const kernel_cap_t *effective,
141141
const kernel_cap_t *inheritable,
142142
const kernel_cap_t *permitted);
143-
extern int cap_bprm_repopulate_creds(struct linux_binprm *bprm);
143+
extern int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file);
144144
extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
145145
const void *value, size_t size, int flags);
146146
extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
@@ -277,7 +277,7 @@ int security_syslog(int type);
277277
int security_settime64(const struct timespec64 *ts, const struct timezone *tz);
278278
int security_vm_enough_memory_mm(struct mm_struct *mm, long pages);
279279
int security_bprm_creds_for_exec(struct linux_binprm *bprm);
280-
int security_bprm_repopulate_creds(struct linux_binprm *bprm);
280+
int security_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file);
281281
int security_bprm_check(struct linux_binprm *bprm);
282282
void security_bprm_committing_creds(struct linux_binprm *bprm);
283283
void security_bprm_committed_creds(struct linux_binprm *bprm);
@@ -575,9 +575,10 @@ static inline int security_bprm_creds_for_exec(struct linux_binprm *bprm)
575575
return 0;
576576
}
577577

578-
static inline int security_bprm_repopulate_creds(struct linux_binprm *bprm)
578+
static inline int security_bprm_creds_from_file(struct linux_binprm *bprm,
579+
struct file *file)
579580
{
580-
return cap_bprm_repopulate_creds(bprm);
581+
return cap_bprm_creds_from_file(bprm, file);
581582
}
582583

583584
static inline int security_bprm_check(struct linux_binprm *bprm)

security/commoncap.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,8 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
647647
* its xattrs and, if present, apply them to the proposed credentials being
648648
* constructed by execve().
649649
*/
650-
static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_fcap)
650+
static int get_file_caps(struct linux_binprm *bprm, struct file *file,
651+
bool *effective, bool *has_fcap)
651652
{
652653
int rc = 0;
653654
struct cpu_vfs_cap_data vcaps;
@@ -657,18 +658,18 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_f
657658
if (!file_caps_enabled)
658659
return 0;
659660

660-
if (!mnt_may_suid(bprm->file->f_path.mnt))
661+
if (!mnt_may_suid(file->f_path.mnt))
661662
return 0;
662663

663664
/*
664665
* This check is redundant with mnt_may_suid() but is kept to make
665666
* explicit that capability bits are limited to s_user_ns and its
666667
* descendants.
667668
*/
668-
if (!current_in_userns(bprm->file->f_path.mnt->mnt_sb->s_user_ns))
669+
if (!current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns))
669670
return 0;
670671

671-
rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps);
672+
rc = get_vfs_caps_from_disk(file->f_path.dentry, &vcaps);
672673
if (rc < 0) {
673674
if (rc == -EINVAL)
674675
printk(KERN_NOTICE "Invalid argument reading file caps for %s\n",
@@ -797,26 +798,27 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old,
797798
}
798799

799800
/**
800-
* cap_bprm_repopulate_creds - Set up the proposed credentials for execve().
801+
* cap_bprm_creds_from_file - Set up the proposed credentials for execve().
801802
* @bprm: The execution parameters, including the proposed creds
803+
* @file: The file to pull the credentials from
802804
*
803805
* Set up the proposed credentials for a new execution context being
804806
* constructed by execve(). The proposed creds in @bprm->cred is altered,
805807
* which won't take effect immediately. Returns 0 if successful, -ve on error.
806808
*/
807-
int cap_bprm_repopulate_creds(struct linux_binprm *bprm)
809+
int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file)
808810
{
811+
/* Process setpcap binaries and capabilities for uid 0 */
809812
const struct cred *old = current_cred();
810813
struct cred *new = bprm->cred;
811814
bool effective = false, has_fcap = false, is_setid;
812815
int ret;
813816
kuid_t root_uid;
814817

815-
new->cap_ambient = old->cap_ambient;
816818
if (WARN_ON(!cap_ambient_invariant_ok(old)))
817819
return -EPERM;
818820

819-
ret = get_file_caps(bprm, &effective, &has_fcap);
821+
ret = get_file_caps(bprm, file, &effective, &has_fcap);
820822
if (ret < 0)
821823
return ret;
822824

@@ -889,7 +891,7 @@ int cap_bprm_repopulate_creds(struct linux_binprm *bprm)
889891
(!__is_real(root_uid, new) &&
890892
(effective ||
891893
__cap_grew(permitted, ambient, new))))
892-
bprm->active_secureexec = 1;
894+
bprm->secureexec = 1;
893895

894896
return 0;
895897
}
@@ -1346,7 +1348,7 @@ static struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
13461348
LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme),
13471349
LSM_HOOK_INIT(capget, cap_capget),
13481350
LSM_HOOK_INIT(capset, cap_capset),
1349-
LSM_HOOK_INIT(bprm_repopulate_creds, cap_bprm_repopulate_creds),
1351+
LSM_HOOK_INIT(bprm_creds_from_file, cap_bprm_creds_from_file),
13501352
LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
13511353
LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
13521354
LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity),

security/security.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,9 +828,9 @@ int security_bprm_creds_for_exec(struct linux_binprm *bprm)
828828
return call_int_hook(bprm_creds_for_exec, 0, bprm);
829829
}
830830

831-
int security_bprm_repopulate_creds(struct linux_binprm *bprm)
831+
int security_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file)
832832
{
833-
return call_int_hook(bprm_repopulate_creds, 0, bprm);
833+
return call_int_hook(bprm_creds_from_file, 0, bprm, file);
834834
}
835835

836836
int security_bprm_check(struct linux_binprm *bprm)

0 commit comments

Comments
 (0)