Skip to content

Commit

Permalink
UPSTREAM: capabilities: ambient capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
NotNoelChannel committed Nov 30, 2023
1 parent 7b8f253 commit f72da7a
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 11 deletions.
5 changes: 4 additions & 1 deletion fs/proc/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,20 +312,23 @@ static void render_cap_t(struct seq_file *m, const char *header,
static inline void task_cap(struct seq_file *m, struct task_struct *p)
{
const struct cred *cred;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective, cap_bset;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective,
cap_bset, cap_ambient;

rcu_read_lock();
cred = __task_cred(p);
cap_inheritable = cred->cap_inheritable;
cap_permitted = cred->cap_permitted;
cap_effective = cred->cap_effective;
cap_bset = cred->cap_bset;
cap_ambient = cred->cap_ambient;
rcu_read_unlock();

render_cap_t(m, "CapInh:\t", &cap_inheritable);
render_cap_t(m, "CapPrm:\t", &cap_permitted);
render_cap_t(m, "CapEff:\t", &cap_effective);
render_cap_t(m, "CapBnd:\t", &cap_bset);
render_cap_t(m, "CapAmb:\t", &cap_ambient);
}

static inline void task_seccomp(struct seq_file *m, struct task_struct *p)
Expand Down
8 changes: 8 additions & 0 deletions include/linux/cred.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ struct cred {
kernel_cap_t cap_permitted; /* caps we're permitted */
kernel_cap_t cap_effective; /* caps we can actually use */
kernel_cap_t cap_bset; /* capability bounding set */
kernel_cap_t cap_ambient; /* Ambient capability set */
#ifdef CONFIG_KEYS
unsigned char jit_keyring; /* default keyring to attach requested
* keys to */
Expand Down Expand Up @@ -197,6 +198,13 @@ static inline void validate_process_creds(void)
}
#endif

static inline bool cap_ambient_invariant_ok(const struct cred *cred)
{
return cap_issubset(cred->cap_ambient,
cap_intersect(cred->cap_permitted,
cred->cap_inheritable));
}

/**
* get_new_cred - Get a reference on a new set of credentials
* @cred: The new credentials to reference
Expand Down
7 changes: 7 additions & 0 deletions include/uapi/linux/prctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,11 @@
#define PR_SET_VMA 0x53564d41
# define PR_SET_VMA_ANON_NAME 0

/* Control the ambient capability set */
#define PR_CAP_AMBIENT 47
# define PR_CAP_AMBIENT_IS_SET 1
# define PR_CAP_AMBIENT_RAISE 2
# define PR_CAP_AMBIENT_LOWER 3
# define PR_CAP_AMBIENT_CLEAR_ALL 4

#endif /* _LINUX_PRCTL_H */
1 change: 1 addition & 0 deletions kernel/user_namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
cred->cap_inheritable = CAP_EMPTY_SET;
cred->cap_permitted = CAP_FULL_SET;
cred->cap_effective = CAP_FULL_SET;
cred->cap_ambient = CAP_EMPTY_SET;
cred->cap_bset = CAP_FULL_SET;
#ifdef CONFIG_KEYS
key_put(cred->request_key_auth);
Expand Down
103 changes: 93 additions & 10 deletions security/commoncap.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,16 @@ int cap_capset(struct cred *new,
new->cap_effective = *effective;
new->cap_inheritable = *inheritable;
new->cap_permitted = *permitted;

/*
* Mask off ambient bits that are no longer both permitted and
* inheritable.
*/
new->cap_ambient = cap_intersect(new->cap_ambient,
cap_intersect(*permitted,
*inheritable));
if (WARN_ON(!cap_ambient_invariant_ok(new)))
return -EINVAL;
return 0;
}

Expand Down Expand Up @@ -380,6 +390,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,

/*
* pP' = (X & fP) | (pI & fI)
* The addition of pA' is handled later.
*/
new->cap_permitted.cap[i] =
(new->cap_bset.cap[i] & permitted) |
Expand Down Expand Up @@ -511,10 +522,13 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
{
const struct cred *old = current_cred();
struct cred *new = bprm->cred;
bool effective, has_cap = false;
bool effective, has_cap = false, is_setid;
int ret;
kuid_t root_uid;

if (WARN_ON(!cap_ambient_invariant_ok(old)))
return -EPERM;

effective = false;
ret = get_file_caps(bprm, &effective, &has_cap);
if (ret < 0)
Expand Down Expand Up @@ -559,8 +573,9 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
*
* In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
*/
if ((!uid_eq(new->euid, old->uid) ||
!gid_eq(new->egid, old->gid) ||
is_setid = !uid_eq(new->euid, old->uid) || !gid_eq(new->egid, old->gid);

if ((is_setid ||
!cap_issubset(new->cap_permitted, old->cap_permitted)) &&
bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
/* downgrade; they get no more than they had, and maybe less */
Expand All @@ -575,11 +590,30 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)

new->suid = new->fsuid = new->euid;
new->sgid = new->fsgid = new->egid;

/* File caps or setid cancels ambient. */
if (has_cap || is_setid)
cap_clear(new->cap_ambient);

/*
* Now that we've computed pA', update pP' to give:
* pP' = (X & fP) | (pI & fI) | pA'
*/
new->cap_permitted = cap_combine(new->cap_permitted, new->cap_ambient);

/*
* Set pE' = (fE ? pP' : pA'). Because pA' is zero if fE is set,
* this is the same as pE' = (fE ? pP' : 0) | pA'.
*/

if (effective)
new->cap_effective = new->cap_permitted;
else
cap_clear(new->cap_effective);
new->cap_effective = new->cap_ambient;

if (WARN_ON(!cap_ambient_invariant_ok(new)))
return -EPERM;

bprm->cap_effective = effective;

/*
Expand All @@ -594,7 +628,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
* Number 1 above might fail if you don't have a full bset, but I think
* that is interesting information to audit.
*/
if (!cap_isclear(new->cap_effective)) {
if (!cap_issubset(new->cap_effective, new->cap_ambient)) {
if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
!uid_eq(new->euid, root_uid) || !uid_eq(new->uid, root_uid) ||
issecure(SECURE_NOROOT)) {
Expand All @@ -605,6 +639,10 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
}

new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);

if (WARN_ON(!cap_ambient_invariant_ok(new)))
return -EPERM;

return 0;
}

Expand All @@ -626,7 +664,7 @@ int cap_bprm_secureexec(struct linux_binprm *bprm)
if (!uid_eq(cred->uid, root_uid)) {
if (bprm->cap_effective)
return 1;
if (!cap_isclear(cred->cap_permitted))
if (!cap_issubset(cred->cap_permitted, cred->cap_ambient))
return 1;
}

Expand Down Expand Up @@ -728,10 +766,18 @@ static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old)
uid_eq(old->suid, root_uid)) &&
(!uid_eq(new->uid, root_uid) &&
!uid_eq(new->euid, root_uid) &&
!uid_eq(new->suid, root_uid)) &&
!issecure(SECURE_KEEP_CAPS)) {
cap_clear(new->cap_permitted);
cap_clear(new->cap_effective);
!uid_eq(new->suid, root_uid))) {
if (!issecure(SECURE_KEEP_CAPS)) {
cap_clear(new->cap_permitted);
cap_clear(new->cap_effective);
}

/*
* Pre-ambient programs expect setresuid to nonroot followed
* by exec to drop capabilities. We should make sure that
* this remains the case.
*/
cap_clear(new->cap_ambient);
}
if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid))
cap_clear(new->cap_effective);
Expand Down Expand Up @@ -979,6 +1025,43 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
else
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
goto changed;

case PR_CAP_AMBIENT:
if (arg2 == PR_CAP_AMBIENT_CLEAR_ALL) {
if (arg3 | arg4 | arg5)
return -EINVAL;

new = prepare_creds();
if (!new)
return -ENOMEM;
cap_clear(new->cap_ambient);
return commit_creds(new);
}

if (((!cap_valid(arg3)) | arg4 | arg5))
return -EINVAL;

if (arg2 == PR_CAP_AMBIENT_IS_SET) {
return !!cap_raised(current_cred()->cap_ambient, arg3);
} else if (arg2 != PR_CAP_AMBIENT_RAISE &&
arg2 != PR_CAP_AMBIENT_LOWER) {
return -EINVAL;
} else {
if (arg2 == PR_CAP_AMBIENT_RAISE &&
(!cap_raised(current_cred()->cap_permitted, arg3) ||
!cap_raised(current_cred()->cap_inheritable,
arg3)))
return -EPERM;

new = prepare_creds();
if (!new)
return -ENOMEM;
if (arg2 == PR_CAP_AMBIENT_RAISE)
cap_raise(new->cap_ambient, arg3);
else
cap_lower(new->cap_ambient, arg3);
return commit_creds(new);
}

default:
/* No functionality available - continue with default */
Expand Down
1 change: 1 addition & 0 deletions security/keys/process_keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,7 @@ void key_change_session_keyring(struct callback_head *twork)
new->cap_inheritable = old->cap_inheritable;
new->cap_permitted = old->cap_permitted;
new->cap_effective = old->cap_effective;
new->cap_ambient = old->cap_ambient;
new->cap_bset = old->cap_bset;

new->jit_keyring = old->jit_keyring;
Expand Down

0 comments on commit f72da7a

Please sign in to comment.