Skip to content
/ linux Public

Commit 17debf5

Browse files
jrjohansengregkh
authored andcommitted
apparmor: fix unprivileged local user can do privileged policy management
commit 6601e13 upstream. 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: b7fd2c0 ("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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 55ef2af commit 17debf5

File tree

3 files changed

+43
-9
lines changed

3 files changed

+43
-9
lines changed

security/apparmor/apparmorfs.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
412412
}
413413

414414
static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
415-
loff_t *pos, struct aa_ns *ns)
415+
loff_t *pos, struct aa_ns *ns,
416+
const struct cred *ocred)
416417
{
417418
struct aa_loaddata *data;
418419
struct aa_label *label;
@@ -423,7 +424,7 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
423424
/* high level check about policy management - fine grained in
424425
* below after unpack
425426
*/
426-
error = aa_may_manage_policy(current_cred(), label, ns, mask);
427+
error = aa_may_manage_policy(current_cred(), label, ns, ocred, mask);
427428
if (error)
428429
goto end_section;
429430

@@ -444,7 +445,8 @@ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
444445
loff_t *pos)
445446
{
446447
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
447-
int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns);
448+
int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns,
449+
f->f_cred);
448450

449451
aa_put_ns(ns);
450452

@@ -462,7 +464,7 @@ static ssize_t profile_replace(struct file *f, const char __user *buf,
462464
{
463465
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
464466
int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
465-
buf, size, pos, ns);
467+
buf, size, pos, ns, f->f_cred);
466468
aa_put_ns(ns);
467469

468470
return error;
@@ -487,7 +489,7 @@ static ssize_t profile_remove(struct file *f, const char __user *buf,
487489
* below after unpack
488490
*/
489491
error = aa_may_manage_policy(current_cred(), label, ns,
490-
AA_MAY_REMOVE_POLICY);
492+
f->f_cred, AA_MAY_REMOVE_POLICY);
491493
if (error)
492494
goto out;
493495

@@ -1819,7 +1821,7 @@ static int ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
18191821
int error;
18201822

18211823
label = begin_current_label_crit_section();
1822-
error = aa_may_manage_policy(current_cred(), label, NULL,
1824+
error = aa_may_manage_policy(current_cred(), label, NULL, NULL,
18231825
AA_MAY_LOAD_POLICY);
18241826
end_current_label_crit_section(label);
18251827
if (error)
@@ -1869,7 +1871,7 @@ static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
18691871
int error;
18701872

18711873
label = begin_current_label_crit_section();
1872-
error = aa_may_manage_policy(current_cred(), label, NULL,
1874+
error = aa_may_manage_policy(current_cred(), label, NULL, NULL,
18731875
AA_MAY_LOAD_POLICY);
18741876
end_current_label_crit_section(label);
18751877
if (error)

security/apparmor/include/policy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ bool aa_policy_admin_capable(const struct cred *subj_cred,
401401
struct aa_label *label, struct aa_ns *ns);
402402
int aa_may_manage_policy(const struct cred *subj_cred,
403403
struct aa_label *label, struct aa_ns *ns,
404-
u32 mask);
404+
const struct cred *ocred, u32 mask);
405405
bool aa_current_policy_view_capable(struct aa_ns *ns);
406406
bool aa_current_policy_admin_capable(struct aa_ns *ns);
407407

security/apparmor/policy.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,17 +891,44 @@ bool aa_current_policy_admin_capable(struct aa_ns *ns)
891891
return res;
892892
}
893893

894+
static bool is_subset_of_obj_privilege(const struct cred *cred,
895+
struct aa_label *label,
896+
const struct cred *ocred)
897+
{
898+
if (cred == ocred)
899+
return true;
900+
901+
if (!aa_label_is_subset(label, cred_label(ocred)))
902+
return false;
903+
/* don't allow crossing userns for now */
904+
if (cred->user_ns != ocred->user_ns)
905+
return false;
906+
if (!cap_issubset(cred->cap_inheritable, ocred->cap_inheritable))
907+
return false;
908+
if (!cap_issubset(cred->cap_permitted, ocred->cap_permitted))
909+
return false;
910+
if (!cap_issubset(cred->cap_effective, ocred->cap_effective))
911+
return false;
912+
if (!cap_issubset(cred->cap_bset, ocred->cap_bset))
913+
return false;
914+
if (!cap_issubset(cred->cap_ambient, ocred->cap_ambient))
915+
return false;
916+
return true;
917+
}
918+
919+
894920
/**
895921
* aa_may_manage_policy - can the current task manage policy
896922
* @subj_cred; subjects cred
897923
* @label: label to check if it can manage policy
898924
* @ns: namespace being managed by @label (may be NULL if @label's ns)
925+
* @ocred: object cred if request is coming from an open object
899926
* @mask: contains the policy manipulation operation being done
900927
*
901928
* Returns: 0 if the task is allowed to manipulate policy else error
902929
*/
903930
int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label,
904-
struct aa_ns *ns, u32 mask)
931+
struct aa_ns *ns, const struct cred *ocred, u32 mask)
905932
{
906933
const char *op;
907934

@@ -917,6 +944,11 @@ int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label,
917944
return audit_policy(label, op, NULL, NULL, "policy_locked",
918945
-EACCES);
919946

947+
if (ocred && !is_subset_of_obj_privilege(subj_cred, label, ocred))
948+
return audit_policy(label, op, NULL, NULL,
949+
"not privileged for target profile",
950+
-EACCES);
951+
920952
if (!aa_policy_admin_capable(subj_cred, label, ns))
921953
return audit_policy(label, op, NULL, NULL, "not policy admin",
922954
-EACCES);

0 commit comments

Comments
 (0)