From nobody Sun Jun 14 10:02:09 2026 Received: from mail-dy1-f228.google.com (mail-dy1-f228.google.com [74.125.82.228]) (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 EF69B36B076 for ; Thu, 2 Apr 2026 05:53:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.228 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775109239; cv=none; b=ENkWGRWaaPUn6BkUOte/gH5zax8P0V95njVzmeKNl1WGiYkFtPGU7vNUMgouUligIV/yoomobHub1whuiuDHancIJHIRZx9j/6KoFWtkDQ9H85Xgll9s4ihSeQf/rQvV4/KDYZOlimYnw2+2H3qoRQywlyf39QwN2axWEJTAlr8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775109239; c=relaxed/simple; bh=0C8xk+345SX9ejuaWyFE+tCyFjEXUYoECt072z2BA2Q=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=UPtrT3axCf2BgMplXmZp1c30iHmlYiN4OyuodY0eOBzBkghIVFsOLMFtjbdJcPir0uzwPQs2JN57fVWqja/aTr4biBX3+7xQFn8DC10rDIBBkjXIo18XJX4cF7kxi40VN1ccMFY1ZRVWcqXiRbFjbaxR+D6+tQ3SApNUMjH4gzY= 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=BG4jFXhk; arc=none smtp.client-ip=74.125.82.228 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="BG4jFXhk" Received: by mail-dy1-f228.google.com with SMTP id 5a478bee46e88-2c16286c496so9625eec.0 for ; Wed, 01 Apr 2026 22:53:56 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775109236; x=1775714036; 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=iVFmR/coGugPGOD5D8t57xuLW0pWKEfclUCRJYgoO14=; b=QcV8haaO2Yrv8IdOXQj5MhRDpIzzoWmXq/OrxOS2Gu/I9mhBe9lPAicJf4Zc4ddYWg IcpuU6ty6t1EtxEAHzxkJOjmamXzkUlA9XjSNOLKsFzaqUcocazQquzhFzXlh5BEIN+h qE7JM8D6+s5pPJLaQBL6GsNWlaEUX1B4X9u5pr7WAaVs/N78d7ItfGFyhzSMZ2e1yWVw NCoBBvFkqoYMjwhvKxOKMSoafaGibnmzDzMqx+kITeXbyb0+b+SWhngKKqH6PNQs+jSB SGyZwsS0uv1sBK1HOCbjwhAswoFciJosc3h5r+WT6CQdtDUUCmN5/LmkPsWPBwhZ/jiP j/tw== X-Forwarded-Encrypted: i=1; AJvYcCXGkuOadYciGMIcfjE+Ap16+n3DCUpycofcDTkOdZogsK5EgXDnBP1b1zjAxYM7ATJy9bVTa+QSX3S0j+8=@vger.kernel.org X-Gm-Message-State: AOJu0Yyd6GbopIreoWvyA3UqpAZfV0ROjUQYoKXvN/TlMokPLhK4Mjkm dZJC1wm89wQgut5qVTZDvetIkjiq+e2kdUUiVNkREZDLvCIILraCF3aAe2JMs1UwhsZ0V1B3KTj PNhltgP86ZgfQ0JTVepotqYiYtrudZPJMlxRrZNNHWNfnb/MO5UtVyUZxzuv9gLjC+IYuYQGw6a OQcpGmTTjgT6aq56oSUVma+l4/PdeXLoJyC3E6xolM7ABlWtY1YypOMmYgcijXpFehTjGsjRbZI Pi9/yK1qH0R8TG/B4yggbhw1O7QqJ3bXPYFfwc= X-Gm-Gg: AeBDieskIVdOq7NbcPDbwRaEyC9e7yITn8dsfTMCsfRo5gHmfsz3In8bFWb7HHzpOh9 7H8EK1GTjN2AkH7FWjRC5gX2XLQDKevu1F1zpvwH6xeJPBb0phrHwaq4Bje3Vn9V4PnEhTS1Njt vawEpbgrnGfsjmie27kYApiFjpBxxkCUQaCOUb/u/2iX8nBC8jFscyBVxYki1iLsy191Z7f4XGe QJAY48j8WldD324pFJhoTdcl2V8Zf4RYyIht16WM9xH5RidFZ3QRFVrMtn3abMGghmyhC6k3xPZ pXRfTmXFFY7MkNnxYkQGo4kD+d+D2yYlkIvhAWsorxXHAhsiaM5+Gj1rSlmzSTeq4jOZzYOgeX4 usl3gOqCnfQJa40jpSPKvBFr2ADtjnO0LSswgFfWEiUJtmSKJIbfuk+4Mx10YFhcj4Wm2INEyHy jxkLLl1+wymXm7EJSrglAOydExKDXLSBJjvLH+N1Q6PT/OxpO1b2rKTlLWmel3wmKYkEbcUSCCB geN X-Received: by 2002:a05:7301:1f17:b0:2bd:db75:c28b with SMTP id 5a478bee46e88-2c934184156mr1391359eec.7.1775109235749; Wed, 01 Apr 2026 22:53:55 -0700 (PDT) Received: from smtp-us-east1-p01-i01-si01.dlp.protect.broadcom.com (address-144-49-247-118.dlp.protect.broadcom.com. [144.49.247.118]) by smtp-relay.gmail.com with ESMTPS id 5a478bee46e88-2ca7c201301sm131348eec.26.2026.04.01.22.53.55 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 01 Apr 2026 22:53:55 -0700 (PDT) X-Relaying-Domain: broadcom.com X-CFilter-Loop: Reflected Received: by mail-dy1-f198.google.com with SMTP id 5a478bee46e88-2c98235c243so103000eec.1 for ; Wed, 01 Apr 2026 22:53:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; t=1775109234; x=1775714034; 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=iVFmR/coGugPGOD5D8t57xuLW0pWKEfclUCRJYgoO14=; b=BG4jFXhkqaG1FgHSP6E1aYT6cI5UPls6SOgBabO/3CAZPFPfSGIhwLCSbQKGKS/kyD 6ovFNNbVobjC7EZ6+GPkqI9eopnFWGomleJXiFMZf5iJ9w+KsWxFNCfC9K8qRUjvNbYt 7e+35gPKfwpgwSX9UC5TSHDdHBL9J+DHz+Hk4= X-Forwarded-Encrypted: i=1; AJvYcCXdwHjdZPzTJRqjRCZiri0ejc25c/3nLFyGb4nUXr6BSjfjplUdriP3WO/druYUe1nhGjdIRVKcvqFoP+0=@vger.kernel.org X-Received: by 2002:a05:7300:d70e:b0:2c4:ec89:bdb with SMTP id 5a478bee46e88-2c930798a42mr1523896eec.2.1775109233684; Wed, 01 Apr 2026 22:53:53 -0700 (PDT) X-Received: by 2002:a05:7300:d70e:b0:2c4:ec89:bdb with SMTP id 5a478bee46e88-2c930798a42mr1523884eec.2.1775109232974; Wed, 01 Apr 2026 22:53:52 -0700 (PDT) Received: from keerthanak-ph5-dev.. ([192.19.161.250]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2ca7cf1271asm2180380eec.26.2026.04.01.22.53.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Apr 2026 22:53:51 -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 v6.1] apparmor: fix unprivileged local user can do privileged policy management Date: Thu, 2 Apr 2026 05:47:00 +0000 Message-ID: <20260402054700.2798707-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 fa518cd82366..fa4a6f20f58e 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -412,7 +412,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; @@ -423,7 +424,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 @@ -444,7 +445,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 @@ -462,7 +464,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; @@ -486,7 +488,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 @@ -1808,7 +1810,7 @@ static int ns_mkdir_op(struct user_namespace *mnt_use= rns, struct inode *dir, 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; @@ -1857,7 +1859,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 639b5b248e63..3f776f5e8de4 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -308,7 +308,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile) bool aa_policy_view_capable(struct aa_label *label, struct aa_ns *ns); bool aa_policy_admin_capable(struct aa_label *label, 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); bool aa_current_policy_view_capable(struct aa_ns *ns); bool aa_current_policy_admin_capable(struct aa_ns *ns); =20 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4ee5a450d118..e7412a221551 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -712,14 +712,42 @@ bool aa_current_policy_admin_capable(struct aa_ns *ns) return res; } =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 @@ -735,6 +763,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 (!aa_policy_admin_capable(label, ns)) return audit_policy(label, op, NULL, NULL, "not policy admin", -EACCES); --=20 2.43.7