From nobody Sun Jun 14 10:01:29 2026 Received: from mail-qv1-f98.google.com (mail-qv1-f98.google.com [209.85.219.98]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 380502FB99D for ; Thu, 2 Apr 2026 05:54:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.98 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775109267; cv=none; b=AFPkBnjIFAJkrvIGSZfxJM5yw3kbG9hJ5vRv90ZSn2XTIyjHemqHR+w/9baYNdkoloOhJuEf5ooImkmSYyRKl5T2r2KKutvQ5IQs/T+IE6z4WNyv3v9fmZY8E3OYtp006GWUk13TXPbz+ceN7K3nB3SLhBC85F5LsitL1hHhYck= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775109267; c=relaxed/simple; bh=6lMYEDk/4SORjeMQTREdR5RVJx70X24NOfnAF6DUR8k=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=b/Xj++0ROg/qnv5CRC5h5+p/F7jTiXcT/679bWEOC+8GCvUlkl7mIX4M7voImpCswDlWDI7VV6xOl1+NScBnbaRgEm4HJMMcWrod0ESsWaD9qHZDA1Iy3L5YS31lZBtIcYZU9fgtAS+HOb8kH6FIIodAYHJd+F5rF3W3wKJren0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=broadcom.com; spf=fail smtp.mailfrom=broadcom.com; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b=NO42xkJM; arc=none smtp.client-ip=209.85.219.98 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=broadcom.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=broadcom.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="NO42xkJM" Received: by mail-qv1-f98.google.com with SMTP id 6a1803df08f44-8a0f848bebfso1015496d6.0 for ; Wed, 01 Apr 2026 22:54:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775109265; x=1775714065; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:dkim-signature:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=un0TrhH26CUN0UWgks1V0cLvA+RLLEZ2+uAxYkzc7Bs=; b=SWm6WFfIsx+0SFogoj7QZePYPnJ92a2Pu6vFTpI7i5BqnZH+HN8QSuz81qWglmUO4o GLWiD/mXgexuNoeaMqu5ALjn1aNIsY/jgaSQbVoCdjaBSx3SX47VJDXCWJgCe54bFs5M 8uW32GeQ0sQqcD4YI6PF/jnVckDJksIPdyRl1fsRnEjCFQrb+2BJkioztn4DXqzwEVK0 I0/JiGbDrKp0/ntP56SH/UugkLclCk9QU8248MhPYspb2AEEUHejOncLEqCo+LxnG7W/ wlhGuKApaQ13E/+SFPOKf9RR0RBmjt/O0GYApP7dEoLKRbWOSFhV6u/Zdnd5pHHNZTQO oubg== X-Forwarded-Encrypted: i=1; AJvYcCVYYg5en+Mr5LT8PNpxm46dTBvEaMYN2lfZVOKfLmluqd8kWG9KKaT0fRjLZJNvPZSvWpT4bw5+sSFzJUo=@vger.kernel.org X-Gm-Message-State: AOJu0YzX7Cv4f4uMD8f6J30AwH3KnJHCHV9eZJqe6y6K651v5ly7Z01m yyHSRirx2YPBOLfXmZJ3LOWXABFST2lhCaVpMNW3at8R/SWJRFiriWenvQBW7yW9MlLSawi2X1h OvOs6sjjQFYcKaEoNhq8+Tfyir1LkRPXUzTD+epgu9gJiUqFY5dZipnC5vNXSrxS8yh2P+RC2aA ahm4sJmRVtKW40VoEVoH53JhUOWN967OJn3431rpaqJk0ks0yi5Z5emrIyE9he5StF6+MH7mm+/ VuI2gSFVtXtQ7q134JQg/LjGqaUFsPq3z+H0Co= X-Gm-Gg: ATEYQzyd8v1MXj0X7takXm/YPOp8dg7rS/SVXD8FBaGrP7GYyyLCRfUZ6+jTAZxUx9w Z+OXT3X9OkmLavaTOoT8c/X2pdn+dgK9fi5I04JxysCgTaUS9R+d7vzrr3tNRmN6v3h6Ym467x4 zz7wDB87H0gbF6+mua8x1AFIqf0tGaSVFnyz5a+hozXDQzQSHmVNJzF5e6BjLOQg4+I0yE6ssgc Ey89jEAe3859wohtmnN/3gJBqIHg6eOfrrF6SMmJro0ZkZjkC924nVot5JMoyf36X9aZWH7TShJ GnQ/VW7lJxDZWzfLva/FMZkysMBLTkk/r/Aa5l1z3bPFjaQ/dgxen0NiRvuz24g2bgbKf1S2+v+ j6mGFsRHG73PQ2tUL8aS0C88Vhiqv9NOs9dBz9Hy99KJYbEW+CaZ8auAUGBd9/fUxDXP84pchXj oOjStKXTK2dVCXNdKxrR6goc6VDKhZHPNG3V4dU1tkU/4HoOVmRZkrCsaF3dv3sabZEbM89cETG Q== X-Received: by 2002:ad4:5aa9:0:b0:89c:d5f4:7b34 with SMTP id 6a1803df08f44-8a43a179116mr65659106d6.4.1775109265071; Wed, 01 Apr 2026 22:54:25 -0700 (PDT) Received: from smtp-us-east1-p01-i01-si01.dlp.protect.broadcom.com (address-144-49-247-15.dlp.protect.broadcom.com. [144.49.247.15]) by smtp-relay.gmail.com with ESMTPS id 6a1803df08f44-8a5942c65dbsm1909766d6.12.2026.04.01.22.54.24 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 01 Apr 2026 22:54:25 -0700 (PDT) X-Relaying-Domain: broadcom.com X-CFilter-Loop: Reflected Received: by mail-dl1-f72.google.com with SMTP id a92af1059eb24-127689b770cso83239c88.3 for ; Wed, 01 Apr 2026 22:54:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; t=1775109264; x=1775714064; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=un0TrhH26CUN0UWgks1V0cLvA+RLLEZ2+uAxYkzc7Bs=; b=NO42xkJMI3wMShk5W+8m8CIF/dAKDcMS8HmLOqjB0FM2Xvk0evmkcw5lceZNpkRBfe KxvYrJckhLAXNWjrLxFYNHJ0vJShpXb2VmQBiYSyqOgl8JrRovfcRQbF+COK7x/TFQFZ gTjyN0EWMjxmohIsV2W8lw7coLEv2D6ynVlyE= X-Forwarded-Encrypted: i=1; AJvYcCWlJ45Hyz+NtmXPRqmBYWVNowkNdQmulsJB6Ylxaok8LAEhdM67w9vv2IX4s0vPEehdSYPc/RSNYnP5RSU=@vger.kernel.org X-Received: by 2002:a05:7300:d50c:b0:2be:681:91b2 with SMTP id 5a478bee46e88-2c9326ad3c3mr1519627eec.6.1775109263569; Wed, 01 Apr 2026 22:54:23 -0700 (PDT) X-Received: by 2002:a05:7300:d50c:b0:2be:681:91b2 with SMTP id 5a478bee46e88-2c9326ad3c3mr1519610eec.6.1775109262899; Wed, 01 Apr 2026 22:54:22 -0700 (PDT) Received: from keerthanak-ph5-dev.. ([192.19.161.250]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2ca7cf126c9sm1634662eec.27.2026.04.01.22.54.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Apr 2026 22:54:22 -0700 (PDT) From: Keerthana K To: stable@vger.kernel.org, gregkh@linuxfoundation.org Cc: john.johansen@canonical.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, georgia.garcia@canonical.com, cengiz.can@canonical.com, sashal@kernel.org, apparmor@lists.ubuntu.com, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, ajay.kaher@broadcom.com, alexey.makhalov@broadcom.com, vamsi-krishna.brahmajosyula@broadcom.com, yin.ding@broadcom.com, tapas.kundu@broadcom.com, Qualys Security Advisory , Salvatore Bonaccorso , Keerthana K Subject: [PATCH v5.10-v5.15] apparmor: fix unprivileged local user can do privileged policy management Date: Thu, 2 Apr 2026 05:47:31 +0000 Message-ID: <20260402054731.2798726-1-keerthana.kalyanasundaram@broadcom.com> X-Mailer: git-send-email 2.43.7 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-DetectorID-Processed: b00c1d49-9d2e-4205-b15f-d015386d3d5e Content-Type: text/plain; charset="utf-8" From: John Johansen commit 6601e13e82841879406bf9f369032656f441a425 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: b7fd2c0340eac ("apparmor: add per policy ns .load, .replace, .remove= interface files") Reported-by: Qualys Security Advisory Tested-by: Salvatore Bonaccorso Reviewed-by: Georgia Garcia Reviewed-by: Cengiz Can Signed-off-by: John Johansen Signed-off-by: Greg Kroah-Hartman [Keerthana: aa_may_manage_policy() does not take a subj_cred parameter (added in 90c436a64a6e, merged in v6.7). Pass current_cred() directly to is_subset_of_obj_privilege() in place of subj_cred, which is equivalent since all call sites pass current_cred() as subj_cred.] Signed-off-by: Keerthana K --- security/apparmor/apparmorfs.c | 16 ++++++++------ security/apparmor/include/policy.h | 2 +- security/apparmor/policy.c | 35 +++++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index e736936f4f0b..3053e5731b02 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -409,7 +409,8 @@ static struct aa_loaddata *aa_simple_write_to_buffer(co= nst char __user *userbuf, } =20 static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, - loff_t *pos, struct aa_ns *ns) + loff_t *pos, struct aa_ns *ns, + const struct cred *ocred) { struct aa_loaddata *data; struct aa_label *label; @@ -420,7 +421,7 @@ static ssize_t policy_update(u32 mask, const char __use= r *buf, size_t size, /* high level check about policy management - fine grained in * below after unpack */ - error =3D aa_may_manage_policy(label, ns, mask); + error =3D aa_may_manage_policy(label, ns, ocred, mask); if (error) goto end_section; =20 @@ -441,7 +442,8 @@ static ssize_t profile_load(struct file *f, const char = __user *buf, size_t size, loff_t *pos) { struct aa_ns *ns =3D aa_get_ns(f->f_inode->i_private); - int error =3D policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns); + int error =3D policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns, + f->f_cred); =20 aa_put_ns(ns); =20 @@ -459,7 +461,7 @@ static ssize_t profile_replace(struct file *f, const ch= ar __user *buf, { struct aa_ns *ns =3D aa_get_ns(f->f_inode->i_private); int error =3D policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY, - buf, size, pos, ns); + buf, size, pos, ns, f->f_cred); aa_put_ns(ns); =20 return error; @@ -483,7 +485,7 @@ static ssize_t profile_remove(struct file *f, const cha= r __user *buf, /* high level check about policy management - fine grained in * below after unpack */ - error =3D aa_may_manage_policy(label, ns, AA_MAY_REMOVE_POLICY); + error =3D aa_may_manage_policy(label, ns, f->f_cred, AA_MAY_REMOVE_POLICY= ); if (error) goto out; =20 @@ -1796,7 +1798,7 @@ static int ns_mkdir_op(struct inode *dir, struct dent= ry *dentry, umode_t mode) int error; =20 label =3D begin_current_label_crit_section(); - error =3D aa_may_manage_policy(label, NULL, AA_MAY_LOAD_POLICY); + error =3D aa_may_manage_policy(label, NULL, NULL, AA_MAY_LOAD_POLICY); end_current_label_crit_section(label); if (error) return error; @@ -1845,7 +1847,7 @@ static int ns_rmdir_op(struct inode *dir, struct dent= ry *dentry) int error; =20 label =3D begin_current_label_crit_section(); - error =3D aa_may_manage_policy(label, NULL, AA_MAY_LOAD_POLICY); + error =3D aa_may_manage_policy(label, NULL, NULL, AA_MAY_LOAD_POLICY); end_current_label_crit_section(label); if (error) return error; diff --git a/security/apparmor/include/policy.h b/security/apparmor/include= /policy.h index b5aa4231af68..f6682a31df23 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -304,6 +304,6 @@ static inline int AUDIT_MODE(struct aa_profile *profile) bool policy_view_capable(struct aa_ns *ns); bool policy_admin_capable(struct aa_ns *ns); int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, - u32 mask); + const struct cred *ocred, u32 mask); =20 #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index e59bdb750ef0..f2bc865bc7b6 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -671,14 +671,42 @@ bool policy_admin_capable(struct aa_ns *ns) return policy_view_capable(ns) && capable && !aa_g_lock_policy; } =20 +static bool is_subset_of_obj_privilege(const struct cred *cred, + struct aa_label *label, + const struct cred *ocred) +{ + if (cred =3D=3D ocred) + return true; + + if (!aa_label_is_subset(label, cred_label(ocred))) + return false; + /* don't allow crossing userns for now */ + if (cred->user_ns !=3D ocred->user_ns) + return false; + if (!cap_issubset(cred->cap_inheritable, ocred->cap_inheritable)) + return false; + if (!cap_issubset(cred->cap_permitted, ocred->cap_permitted)) + return false; + if (!cap_issubset(cred->cap_effective, ocred->cap_effective)) + return false; + if (!cap_issubset(cred->cap_bset, ocred->cap_bset)) + return false; + if (!cap_issubset(cred->cap_ambient, ocred->cap_ambient)) + return false; + return true; +} + + /** * aa_may_manage_policy - can the current task manage policy * @label: label to check if it can manage policy + * @ocred: object cred if request is coming from an open object * @op: the policy manipulation operation being done * * Returns: 0 if the task is allowed to manipulate policy else error */ -int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, u32 mas= k) +int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, + const struct cred *ocred, u32 mask) { const char *op; =20 @@ -694,6 +722,11 @@ int aa_may_manage_policy(struct aa_label *label, struc= t aa_ns *ns, u32 mask) return audit_policy(label, op, NULL, NULL, "policy_locked", -EACCES); =20 + if (ocred && !is_subset_of_obj_privilege(current_cred(), label, ocred)) + return audit_policy(label, op, NULL, NULL, + "not privileged for target profile", + -EACCES); + if (!policy_admin_capable(ns)) return audit_policy(label, op, NULL, NULL, "not policy admin", -EACCES); --=20 2.43.7