Linux 6.9-rc1
[linux-2.6-microblaze.git] / ipc / ipc_sysctl.c
index ef313ec..45cb1da 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/ipc_namespace.h>
 #include <linux/msg.h>
 #include <linux/slab.h>
+#include <linux/cred.h>
 #include "util.h"
 
 static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
@@ -190,25 +191,57 @@ static int set_is_seen(struct ctl_table_set *set)
        return &current->nsproxy->ipc_ns->ipc_set == set;
 }
 
+static void ipc_set_ownership(struct ctl_table_header *head,
+                             struct ctl_table *table,
+                             kuid_t *uid, kgid_t *gid)
+{
+       struct ipc_namespace *ns =
+               container_of(head->set, struct ipc_namespace, ipc_set);
+
+       kuid_t ns_root_uid = make_kuid(ns->user_ns, 0);
+       kgid_t ns_root_gid = make_kgid(ns->user_ns, 0);
+
+       *uid = uid_valid(ns_root_uid) ? ns_root_uid : GLOBAL_ROOT_UID;
+       *gid = gid_valid(ns_root_gid) ? ns_root_gid : GLOBAL_ROOT_GID;
+}
+
 static int ipc_permissions(struct ctl_table_header *head, struct ctl_table *table)
 {
        int mode = table->mode;
 
 #ifdef CONFIG_CHECKPOINT_RESTORE
-       struct ipc_namespace *ns = current->nsproxy->ipc_ns;
+       struct ipc_namespace *ns =
+               container_of(head->set, struct ipc_namespace, ipc_set);
 
        if (((table->data == &ns->ids[IPC_SEM_IDS].next_id) ||
             (table->data == &ns->ids[IPC_MSG_IDS].next_id) ||
             (table->data == &ns->ids[IPC_SHM_IDS].next_id)) &&
            checkpoint_restore_ns_capable(ns->user_ns))
                mode = 0666;
+       else
 #endif
-       return mode;
+       {
+               kuid_t ns_root_uid;
+               kgid_t ns_root_gid;
+
+               ipc_set_ownership(head, table, &ns_root_uid, &ns_root_gid);
+
+               if (uid_eq(current_euid(), ns_root_uid))
+                       mode >>= 6;
+
+               else if (in_egroup_p(ns_root_gid))
+                       mode >>= 3;
+       }
+
+       mode &= 7;
+
+       return (mode << 6) | (mode << 3) | mode;
 }
 
 static struct ctl_table_root set_root = {
        .lookup = set_lookup,
        .permissions = ipc_permissions,
+       .set_ownership = ipc_set_ownership,
 };
 
 bool setup_ipc_sysctls(struct ipc_namespace *ns)
@@ -259,7 +292,8 @@ bool setup_ipc_sysctls(struct ipc_namespace *ns)
                                tbl[i].data = NULL;
                }
 
-               ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl);
+               ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl,
+                                                         ARRAY_SIZE(ipc_sysctls));
        }
        if (!ns->ipc_sysctls) {
                kfree(tbl);