Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

apparmor: fix unprivileged local user can do privileged policy management

An unprivileged local user can load, replace, and remove profiles by
opening the apparmorfs interfaces, via a confused deputy attack, by
passing the opened fd to a privileged process, and getting the
privileged process to write to the interface.

This does require a privileged target that can be manipulated to do
the write for the unprivileged process, but once such access is
achieved full policy management is possible and all the possible
implications that implies: removing confinement, DoS of system or
target applications by denying all execution, by-passing the
unprivileged user namespace restriction, to exploiting kernel bugs for
a local privilege escalation.

The policy management interface can not have its permissions simply
changed from 0666 to 0600 because non-root processes need to be able
to load policy to different policy namespaces.

Instead ensure the task writing the interface has privileges that
are a subset of the task that opened the interface. This is already
done via policy for confined processes, but unconfined can delegate
access to the opened fd, by-passing the usual policy check.

Fixes: b7fd2c0340eac ("apparmor: add per policy ns .load, .replace, .remove interface files")
Reported-by: Qualys Security Advisory <qsa@qualys.com>
Tested-by: Salvatore Bonaccorso <carnil@debian.org>
Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Reviewed-by: Cengiz Can <cengiz.can@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>

+43 -9
+9 -7
security/apparmor/apparmorfs.c
··· 417 417 } 418 418 419 419 static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, 420 - loff_t *pos, struct aa_ns *ns) 420 + loff_t *pos, struct aa_ns *ns, 421 + const struct cred *ocred) 421 422 { 422 423 struct aa_loaddata *data; 423 424 struct aa_label *label; ··· 429 428 /* high level check about policy management - fine grained in 430 429 * below after unpack 431 430 */ 432 - error = aa_may_manage_policy(current_cred(), label, ns, mask); 431 + error = aa_may_manage_policy(current_cred(), label, ns, ocred, mask); 433 432 if (error) 434 433 goto end_section; 435 434 ··· 450 449 loff_t *pos) 451 450 { 452 451 struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); 453 - int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns); 452 + int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns, 453 + f->f_cred); 454 454 455 455 aa_put_ns(ns); 456 456 ··· 469 467 { 470 468 struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); 471 469 int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY, 472 - buf, size, pos, ns); 470 + buf, size, pos, ns, f->f_cred); 473 471 aa_put_ns(ns); 474 472 475 473 return error; ··· 494 492 * below after unpack 495 493 */ 496 494 error = aa_may_manage_policy(current_cred(), label, ns, 497 - AA_MAY_REMOVE_POLICY); 495 + f->f_cred, AA_MAY_REMOVE_POLICY); 498 496 if (error) 499 497 goto out; 500 498 ··· 1832 1830 int error; 1833 1831 1834 1832 label = begin_current_label_crit_section(); 1835 - error = aa_may_manage_policy(current_cred(), label, NULL, 1833 + error = aa_may_manage_policy(current_cred(), label, NULL, NULL, 1836 1834 AA_MAY_LOAD_POLICY); 1837 1835 end_current_label_crit_section(label); 1838 1836 if (error) ··· 1882 1880 int error; 1883 1881 1884 1882 label = begin_current_label_crit_section(); 1885 - error = aa_may_manage_policy(current_cred(), label, NULL, 1883 + error = aa_may_manage_policy(current_cred(), label, NULL, NULL, 1886 1884 AA_MAY_LOAD_POLICY); 1887 1885 end_current_label_crit_section(label); 1888 1886 if (error)
+1 -1
security/apparmor/include/policy.h
··· 443 443 struct aa_label *label, struct aa_ns *ns); 444 444 int aa_may_manage_policy(const struct cred *subj_cred, 445 445 struct aa_label *label, struct aa_ns *ns, 446 - u32 mask); 446 + const struct cred *ocred, u32 mask); 447 447 bool aa_current_policy_view_capable(struct aa_ns *ns); 448 448 bool aa_current_policy_admin_capable(struct aa_ns *ns); 449 449
+33 -1
security/apparmor/policy.c
··· 942 942 return res; 943 943 } 944 944 945 + static bool is_subset_of_obj_privilege(const struct cred *cred, 946 + struct aa_label *label, 947 + const struct cred *ocred) 948 + { 949 + if (cred == ocred) 950 + return true; 951 + 952 + if (!aa_label_is_subset(label, cred_label(ocred))) 953 + return false; 954 + /* don't allow crossing userns for now */ 955 + if (cred->user_ns != ocred->user_ns) 956 + return false; 957 + if (!cap_issubset(cred->cap_inheritable, ocred->cap_inheritable)) 958 + return false; 959 + if (!cap_issubset(cred->cap_permitted, ocred->cap_permitted)) 960 + return false; 961 + if (!cap_issubset(cred->cap_effective, ocred->cap_effective)) 962 + return false; 963 + if (!cap_issubset(cred->cap_bset, ocred->cap_bset)) 964 + return false; 965 + if (!cap_issubset(cred->cap_ambient, ocred->cap_ambient)) 966 + return false; 967 + return true; 968 + } 969 + 970 + 945 971 /** 946 972 * aa_may_manage_policy - can the current task manage policy 947 973 * @subj_cred: subjects cred 948 974 * @label: label to check if it can manage policy 949 975 * @ns: namespace being managed by @label (may be NULL if @label's ns) 976 + * @ocred: object cred if request is coming from an open object 950 977 * @mask: contains the policy manipulation operation being done 951 978 * 952 979 * Returns: 0 if the task is allowed to manipulate policy else error 953 980 */ 954 981 int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label, 955 - struct aa_ns *ns, u32 mask) 982 + struct aa_ns *ns, const struct cred *ocred, u32 mask) 956 983 { 957 984 const char *op; 958 985 ··· 993 966 /* check if loading policy is locked out */ 994 967 if (aa_g_lock_policy) 995 968 return audit_policy(label, op, NULL, NULL, "policy_locked", 969 + -EACCES); 970 + 971 + if (ocred && !is_subset_of_obj_privilege(subj_cred, label, ocred)) 972 + return audit_policy(label, op, NULL, NULL, 973 + "not privileged for target profile", 996 974 -EACCES); 997 975 998 976 if (!aa_policy_admin_capable(subj_cred, label, ns))