Enable users to manage AppArmor policies through the new hooks
lsm_config_self_policy and lsm_config_system_policy.
lsm_config_self_policy allows stacking existing policies in the kernel.
This ensures that it can only further restrict the caller and can never
be used to gain new privileges.
lsm_config_system_policy allows loading or replacing AppArmor policies in
any AppArmor namespace.
Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>
---
security/apparmor/apparmorfs.c | 31 +++++++++++++
security/apparmor/include/apparmorfs.h | 3 ++
security/apparmor/lsm.c | 63 ++++++++++++++++++++++++++
3 files changed, 97 insertions(+)
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 6039afae4bfc..827fe06b20ac 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -439,6 +439,37 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
return error;
}
+/**
+ * aa_profile_load_ns_name - load a profile into the current namespace identified by name
+ * @name The name of the namesapce to load the policy in. "" for root_ns
+ * @name_size size of @name. 0 For root ns
+ * @buf buffer containing the user-provided policy
+ * @size size of @buf
+ * @ppos position pointer in the file
+ *
+ * Returns: 0 on success, negative value on error
+ */
+ssize_t aa_profile_load_ns_name(char *name, size_t name_size, const void __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct aa_ns *ns;
+
+ if (name_size == 0)
+ ns = aa_get_ns(root_ns);
+ else
+ ns = aa_lookupn_ns(root_ns, name, name_size);
+
+ if (!ns)
+ return -EINVAL;
+
+ int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
+ buf, size, ppos, ns);
+
+ aa_put_ns(ns);
+
+ return error >= 0 ? 0 : error;
+}
+
/* .load file hook fn to load policy */
static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
loff_t *pos)
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 1e94904f68d9..fd415afb7659 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -112,6 +112,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
void __aafs_ns_rmdir(struct aa_ns *ns);
int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name,
struct dentry *dent);
+ssize_t aa_profile_load_ns_name(char *name, size_t name_len, const void __user *buf,
+ size_t size, loff_t *ppos);
+
struct aa_loaddata;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 9b6c2f157f83..b38c4926cdc2 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1275,6 +1275,65 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
}
+/**
+ * apparmor_lsm_config_self_policy - Stack a profile
+ * @buf: buffer containing the user-provided name of the profile to stack
+ * @size: size of @buf
+ *
+ * Returns: 0 on success, negative value on error
+ */
+static int apparmor_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ char *name = kvmalloc(size, GFP_KERNEL);
+ long name_size;
+ int ret;
+
+ if (op != LSM_POLICY_LOAD || flags)
+ return -EOPNOTSUPP;
+
+ name_size = strncpy_from_user(name, buf, size);
+ if (name_size < 0)
+ return name_size;
+
+ ret = aa_change_profile(name, AA_CHANGE_STACK);
+
+ kvfree(name);
+
+ return ret;
+}
+
+/**
+ * apparmor_lsm_config_system_policy - Load or replace a system policy
+ * @buf: user-supplied buffer in the form "<ns>\0<policy>"
+ * <ns> is the namespace to load the policy into (empty string for root)
+ * <policy> is the policy to load
+ * then '\0' then the policy to load
+ * @size: size of @buf
+ *
+ * Returns: 0 on success, negative value on error
+ */
+static int apparmor_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf,
+ size_t size, u32 flags)
+{
+ loff_t pos = 0; // Partial writing is not currently supported
+ char name[256];
+ long name_size;
+
+ if (op != LSM_POLICY_LOAD || flags)
+ return -EOPNOTSUPP;
+
+ name_size = strncpy_from_user(name, buf, 256);
+ if (name_size < 0)
+ return name_size;
+ else if (name_size == 256)
+ return -E2BIG;
+
+ return aa_profile_load_ns_name(name, name_size, buf + name_size + 1,
+ size - name_size - 1, &pos);
+}
+
+
#ifdef CONFIG_NETWORK_SECMARK
/**
* apparmor_socket_sock_rcv_skb - check perms before associating skb to sk
@@ -1483,6 +1542,10 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
+
+ LSM_HOOK_INIT(lsm_config_self_policy, apparmor_lsm_config_self_policy),
+ LSM_HOOK_INIT(lsm_config_system_policy,
+ apparmor_lsm_config_system_policy),
#ifdef CONFIG_NETWORK_SECMARK
LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb),
#endif
--
2.48.1
Hi Maxime, kernel test robot noticed the following build warnings: [auto build test WARNING on 9c32cda43eb78f78c73aee4aa344b777714e259b] url: https://github.com/intel-lab-lkp/linux/commits/Maxime-B-lair/Wire-up-lsm_config_self_policy-and-lsm_config_system_policy-syscalls/20250620-022714 base: 9c32cda43eb78f78c73aee4aa344b777714e259b patch link: https://lore.kernel.org/r/20250619181600.478038-4-maxime.belair%40canonical.com patch subject: [PATCH v2 3/3] AppArmor: add support for lsm_config_self_policy and lsm_config_system_policy config: x86_64-buildonly-randconfig-002-20250620 (https://download.01.org/0day-ci/archive/20250620/202506201414.tHOEthTb-lkp@intel.com/config) compiler: gcc-12 (Debian 12.2.0-14) 12.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250620/202506201414.tHOEthTb-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202506201414.tHOEthTb-lkp@intel.com/ All warnings (new ones prefixed by >>): >> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'name' not described in 'aa_profile_load_ns_name' >> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'name_size' not described in 'aa_profile_load_ns_name' >> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'buf' not described in 'aa_profile_load_ns_name' >> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'size' not described in 'aa_profile_load_ns_name' >> security/apparmor/apparmorfs.c:454: warning: Function parameter or struct member 'ppos' not described in 'aa_profile_load_ns_name' -- >> security/apparmor/lsm.c:1287: warning: Function parameter or struct member 'lsm_id' not described in 'apparmor_lsm_config_self_policy' >> security/apparmor/lsm.c:1287: warning: Function parameter or struct member 'op' not described in 'apparmor_lsm_config_self_policy' >> security/apparmor/lsm.c:1287: warning: Function parameter or struct member 'flags' not described in 'apparmor_lsm_config_self_policy' >> security/apparmor/lsm.c:1318: warning: Function parameter or struct member 'lsm_id' not described in 'apparmor_lsm_config_system_policy' >> security/apparmor/lsm.c:1318: warning: Function parameter or struct member 'op' not described in 'apparmor_lsm_config_system_policy' >> security/apparmor/lsm.c:1318: warning: Function parameter or struct member 'flags' not described in 'apparmor_lsm_config_system_policy' vim +454 security/apparmor/apparmorfs.c 441 442 /** 443 * aa_profile_load_ns_name - load a profile into the current namespace identified by name 444 * @name The name of the namesapce to load the policy in. "" for root_ns 445 * @name_size size of @name. 0 For root ns 446 * @buf buffer containing the user-provided policy 447 * @size size of @buf 448 * @ppos position pointer in the file 449 * 450 * Returns: 0 on success, negative value on error 451 */ 452 ssize_t aa_profile_load_ns_name(char *name, size_t name_size, const void __user *buf, 453 size_t size, loff_t *ppos) > 454 { 455 struct aa_ns *ns; 456 457 if (name_size == 0) 458 ns = aa_get_ns(root_ns); 459 else 460 ns = aa_lookupn_ns(root_ns, name, name_size); 461 462 if (!ns) 463 return -EINVAL; 464 465 int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY, 466 buf, size, ppos, ns); 467 468 aa_put_ns(ns); 469 470 return error >= 0 ? 0 : error; 471 } 472 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
On 6/19/25 11:15 AM, Maxime Bélair wrote: > Enable users to manage AppArmor policies through the new hooks > lsm_config_self_policy and lsm_config_system_policy. > > lsm_config_self_policy allows stacking existing policies in the kernel. > This ensures that it can only further restrict the caller and can never > be used to gain new privileges. > > lsm_config_system_policy allows loading or replacing AppArmor policies in > any AppArmor namespace. > > Signed-off-by: Maxime Bélair <maxime.belair@canonical.com> > --- > security/apparmor/apparmorfs.c | 31 +++++++++++++ > security/apparmor/include/apparmorfs.h | 3 ++ > security/apparmor/lsm.c | 63 ++++++++++++++++++++++++++ > 3 files changed, 97 insertions(+) > > diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c > index 6039afae4bfc..827fe06b20ac 100644 > --- a/security/apparmor/apparmorfs.c > +++ b/security/apparmor/apparmorfs.c > @@ -439,6 +439,37 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, > return error; > } > > +/** > + * aa_profile_load_ns_name - load a profile into the current namespace identified by name > + * @name The name of the namesapce to load the policy in. "" for root_ns > + * @name_size size of @name. 0 For root ns > + * @buf buffer containing the user-provided policy > + * @size size of @buf > + * @ppos position pointer in the file Please use proper kernel-doc syntax above. Each @var_name should be followed by a ':'. > + * > + * Returns: 0 on success, negative value on error > + */ > +ssize_t aa_profile_load_ns_name(char *name, size_t name_size, const void __user *buf, > + size_t size, loff_t *ppos) > +{ > + struct aa_ns *ns; > + > + if (name_size == 0) > + ns = aa_get_ns(root_ns); > + else > + ns = aa_lookupn_ns(root_ns, name, name_size); > + > + if (!ns) > + return -EINVAL; > + > + int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY, > + buf, size, ppos, ns); > + > + aa_put_ns(ns); > + > + return error >= 0 ? 0 : error; > +} > + > /* .load file hook fn to load policy */ > static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, > loff_t *pos) > diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c > index 9b6c2f157f83..b38c4926cdc2 100644 > --- a/security/apparmor/lsm.c > +++ b/security/apparmor/lsm.c > @@ -1275,6 +1275,65 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) > return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock); > } > > +/** > + * apparmor_lsm_config_self_policy - Stack a profile > + * @buf: buffer containing the user-provided name of the profile to stack > + * @size: size of @buf Describe all function parameters in kernel-doc above. > + * > + * Returns: 0 on success, negative value on error > + */ > +static int apparmor_lsm_config_self_policy(u32 lsm_id, u32 op, void __user *buf, > + size_t size, u32 flags) > +{ > + char *name = kvmalloc(size, GFP_KERNEL); Check return value (i.e., name) before use? > + long name_size;> + int ret; > + > + if (op != LSM_POLICY_LOAD || flags) > + return -EOPNOTSUPP; > + > + name_size = strncpy_from_user(name, buf, size); > + if (name_size < 0) > + return name_size; > + > + ret = aa_change_profile(name, AA_CHANGE_STACK); > + > + kvfree(name); > + > + return ret; > +} > + > +/** > + * apparmor_lsm_config_system_policy - Load or replace a system policy > + * @buf: user-supplied buffer in the form "<ns>\0<policy>" > + * <ns> is the namespace to load the policy into (empty string for root) > + * <policy> is the policy to load > + * then '\0' then the policy to load Misplaced line? > + * @size: size of @buf Please describe all function parameters in kernel-doc notation. > + * > + * Returns: 0 on success, negative value on error > + */ > +static int apparmor_lsm_config_system_policy(u32 lsm_id, u32 op, void __user *buf, > + size_t size, u32 flags) > +{ [snip] -- ~Randy
© 2016 - 2025 Red Hat, Inc.