|
@@ -267,6 +267,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;
|
|
|
}
|
|
|
|
|
@@ -347,6 +357,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) |
|
|
@@ -474,10 +485,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)
|
|
@@ -522,8 +536,9 @@ skip:
|
|
|
*
|
|
|
* 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 */
|
|
@@ -539,10 +554,28 @@ skip:
|
|
|
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;
|
|
|
|
|
|
/*
|
|
@@ -557,7 +590,7 @@ skip:
|
|
|
* 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)) {
|
|
@@ -568,6 +601,10 @@ skip:
|
|
|
}
|
|
|
|
|
|
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
|
|
|
+
|
|
|
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -589,7 +626,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;
|
|
|
}
|
|
|
|
|
@@ -691,10 +728,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);
|
|
@@ -924,6 +969,43 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|
|
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
|
|
|
return commit_creds(new);
|
|
|
|
|
|
+ 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 */
|
|
|
return -ENOSYS;
|