]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
apparmor: remove or add symlinks to rawdata according to export_binary
authorGeorgia Garcia <georgia.garcia@canonical.com>
Thu, 29 Jan 2026 18:39:42 +0000 (15:39 -0300)
committerJohn Johansen <john.johansen@canonical.com>
Sun, 14 Jun 2026 03:14:07 +0000 (20:14 -0700)
When the export_binary parameter is set, then rawdata is available and
there should be a symbolic link for the rawdata in the profile
directory in apparmorfs. If the parameter is unset, then the symlinks
should not exist.

The issue arises when changing the value of export_binary on runtime
and replacing profiles. If export_binary was set when the profile was
originally loaded, then changed to 0 and the profile was reloaded,
then the symbolic links would still exist but would return ENOENT
because the rawdata no longer exists.

On the opposite side, if export_binary was unset when the profile was
originally loaded, then changed to 1 and the profile was reloaded,
then the symbolic links would not exist, even though the rawdata does.

Fixes: d61c57fde8191 ("apparmor: make export of raw binary profile to userspace optional")
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/apparmorfs.c
security/apparmor/include/apparmorfs.h
security/apparmor/policy.c

index 03fc18cf5fa7093aab031c31353599210fa35cdc..a59c4b83620f3e045f8aa011c3c74f2c33e5c043 100644 (file)
@@ -1757,6 +1757,80 @@ static const struct inode_operations rawdata_link_abi_iops = {
 static const struct inode_operations rawdata_link_data_iops = {
        .get_link       = rawdata_get_link_data,
 };
+
+/*
+ * Requires: @profile->ns->lock held
+ */
+void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile)
+{
+       aafs_remove(profile->dents[AAFS_PROF_RAW_HASH]);
+       profile->dents[AAFS_PROF_RAW_HASH] = NULL;
+       aafs_remove(profile->dents[AAFS_PROF_RAW_ABI]);
+       profile->dents[AAFS_PROF_RAW_ABI] = NULL;
+       aafs_remove(profile->dents[AAFS_PROF_RAW_DATA]);
+       profile->dents[AAFS_PROF_RAW_DATA] = NULL;
+}
+
+static inline int create_symlink_dent(struct aa_profile *profile,
+                                     const char *name,
+                                     enum aafs_prof_type type,
+                                     const struct inode_operations *iops)
+{
+       struct dentry *dent = NULL;
+       struct dentry *dir = prof_dir(profile);
+
+       if (profile->dents[type])
+               return 0;
+
+       dent = aafs_create(name, S_IFLNK | 0444, dir,
+                          &profile->label.proxy->count, NULL, NULL, iops);
+       if (IS_ERR(dent))
+               return PTR_ERR(dent);
+
+       profile->dents[type] = dent;
+       return 0;
+}
+
+/*
+ * Requires: @profile->ns->lock held
+ */
+int __aa_create_rawdata_symlink_dents(struct aa_profile *profile)
+{
+       int error;
+
+       if (!profile ||
+           (profile->dents[AAFS_PROF_RAW_HASH] &&
+            profile->dents[AAFS_PROF_RAW_ABI] &&
+            profile->dents[AAFS_PROF_RAW_DATA]))
+               return 0;
+
+       if (!profile->rawdata)
+               return 0;
+
+       if (aa_g_hash_policy) {
+               error = create_symlink_dent(profile, "raw_sha256",
+                                           AAFS_PROF_RAW_HASH,
+                                           &rawdata_link_sha256_iops);
+               if (error)
+                       return error;
+       }
+
+       error = create_symlink_dent(profile, "raw_abi",
+                                   AAFS_PROF_RAW_ABI,
+                                   &rawdata_link_abi_iops);
+       if (error)
+               return error;
+
+
+       error = create_symlink_dent(profile, "raw_data",
+                                   AAFS_PROF_RAW_DATA,
+                                   &rawdata_link_data_iops);
+       if (error)
+               return error;
+
+       return 0;
+}
+
 #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
 
 /*
@@ -1832,31 +1906,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
                profile->dents[AAFS_PROF_HASH] = dent;
        }
 
-#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
-       if (profile->rawdata) {
-               if (aa_g_hash_policy) {
-                       dent = aafs_create("raw_sha256", S_IFLNK | 0444, dir,
-                                          &profile->label.proxy->count, NULL,
-                                          NULL, &rawdata_link_sha256_iops);
-                       if (IS_ERR(dent))
-                               goto fail;
-                       profile->dents[AAFS_PROF_RAW_HASH] = dent;
-               }
-               dent = aafs_create("raw_abi", S_IFLNK | 0444, dir,
-                                  &profile->label.proxy->count, NULL, NULL,
-                                  &rawdata_link_abi_iops);
-               if (IS_ERR(dent))
-                       goto fail;
-               profile->dents[AAFS_PROF_RAW_ABI] = dent;
-
-               dent = aafs_create("raw_data", S_IFLNK | 0444, dir,
-                                  &profile->label.proxy->count, NULL, NULL,
-                                  &rawdata_link_data_iops);
-               if (IS_ERR(dent))
-                       goto fail;
-               profile->dents[AAFS_PROF_RAW_DATA] = dent;
-       }
-#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
+       error = __aa_create_rawdata_symlink_dents(profile);
+       if (error)
+               goto fail2;
 
        list_for_each_entry(child, &profile->base.profiles, base.list) {
                error = __aafs_profile_mkdir(child, prof_child_dir(profile));
index dd580594dfb70a64f4f7f0110305d13c58ea1034..33243d11fd10604d42d32cf88d109267bdbe210a 100644 (file)
@@ -120,6 +120,8 @@ struct aa_loaddata;
 #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
 void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata);
 int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata);
+void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile);
+int __aa_create_rawdata_symlink_dents(struct aa_profile *profile);
 #else
 static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
 {
@@ -131,6 +133,16 @@ static inline int __aa_fs_create_rawdata(struct aa_ns *ns,
 {
        return 0;
 }
+
+static inline void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile)
+{
+       /* empty stub */
+}
+
+static inline int __aa_create_rawdata_symlink_dents(struct aa_profile *profile)
+{
+       return 0;
+}
 #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
 
 #endif /* __AA_APPARMORFS_H */
index 08620984d9505f646c2a207d97fc717565d13427..847b0ff450c511c210b2d50db0bc3bd9db63b646 100644 (file)
@@ -1349,6 +1349,16 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
                        goto skip;
                }
 
+               if (!aa_g_export_binary) {
+                       if (ent->old && ent->old->rawdata &&
+                           ent->old->dents[AAFS_LOADDATA_DIR]) {
+                               /* remove rawdata symlinks because the symlink
+                                * target will be removed
+                                */
+                               __aa_remove_rawdata_symlink_dents(ent->old);
+                       }
+               }
+
                /*
                 * TODO: finer dedup based on profile range in data. Load set
                 * can differ but profile may remain unchanged
@@ -1359,6 +1369,11 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
                if (ent->old) {
                        share_name(ent->old, ent->new);
                        __replace_profile(ent->old, ent->new);
+                       if (aa_g_export_binary) {
+                               /* recreate rawdata symlinks */
+                               if (!ent->old->rawdata)
+                                       __aa_create_rawdata_symlink_dents(ent->new);
+                       }
                } else {
                        struct list_head *lh;