168 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2007 IBM Corporation4 *5 * Author: Cedric Le Goater <clg@fr.ibm.com>6 */7 8#include <linux/nsproxy.h>9#include <linux/ipc_namespace.h>10#include <linux/sysctl.h>11 12#include <linux/stat.h>13#include <linux/capability.h>14#include <linux/slab.h>15#include <linux/cred.h>16 17static int msg_max_limit_min = MIN_MSGMAX;18static int msg_max_limit_max = HARD_MSGMAX;19 20static int msg_maxsize_limit_min = MIN_MSGSIZEMAX;21static int msg_maxsize_limit_max = HARD_MSGSIZEMAX;22 23static struct ctl_table mq_sysctls[] = {24 {25 .procname = "queues_max",26 .data = &init_ipc_ns.mq_queues_max,27 .maxlen = sizeof(int),28 .mode = 0644,29 .proc_handler = proc_dointvec,30 },31 {32 .procname = "msg_max",33 .data = &init_ipc_ns.mq_msg_max,34 .maxlen = sizeof(int),35 .mode = 0644,36 .proc_handler = proc_dointvec_minmax,37 .extra1 = &msg_max_limit_min,38 .extra2 = &msg_max_limit_max,39 },40 {41 .procname = "msgsize_max",42 .data = &init_ipc_ns.mq_msgsize_max,43 .maxlen = sizeof(int),44 .mode = 0644,45 .proc_handler = proc_dointvec_minmax,46 .extra1 = &msg_maxsize_limit_min,47 .extra2 = &msg_maxsize_limit_max,48 },49 {50 .procname = "msg_default",51 .data = &init_ipc_ns.mq_msg_default,52 .maxlen = sizeof(int),53 .mode = 0644,54 .proc_handler = proc_dointvec_minmax,55 .extra1 = &msg_max_limit_min,56 .extra2 = &msg_max_limit_max,57 },58 {59 .procname = "msgsize_default",60 .data = &init_ipc_ns.mq_msgsize_default,61 .maxlen = sizeof(int),62 .mode = 0644,63 .proc_handler = proc_dointvec_minmax,64 .extra1 = &msg_maxsize_limit_min,65 .extra2 = &msg_maxsize_limit_max,66 },67};68 69static struct ctl_table_set *set_lookup(struct ctl_table_root *root)70{71 return ¤t->nsproxy->ipc_ns->mq_set;72}73 74static int set_is_seen(struct ctl_table_set *set)75{76 return ¤t->nsproxy->ipc_ns->mq_set == set;77}78 79static void mq_set_ownership(struct ctl_table_header *head,80 kuid_t *uid, kgid_t *gid)81{82 struct ipc_namespace *ns =83 container_of(head->set, struct ipc_namespace, mq_set);84 85 kuid_t ns_root_uid = make_kuid(ns->user_ns, 0);86 kgid_t ns_root_gid = make_kgid(ns->user_ns, 0);87 88 *uid = uid_valid(ns_root_uid) ? ns_root_uid : GLOBAL_ROOT_UID;89 *gid = gid_valid(ns_root_gid) ? ns_root_gid : GLOBAL_ROOT_GID;90}91 92static int mq_permissions(struct ctl_table_header *head, const struct ctl_table *table)93{94 int mode = table->mode;95 kuid_t ns_root_uid;96 kgid_t ns_root_gid;97 98 mq_set_ownership(head, &ns_root_uid, &ns_root_gid);99 100 if (uid_eq(current_euid(), ns_root_uid))101 mode >>= 6;102 103 else if (in_egroup_p(ns_root_gid))104 mode >>= 3;105 106 mode &= 7;107 108 return (mode << 6) | (mode << 3) | mode;109}110 111static struct ctl_table_root set_root = {112 .lookup = set_lookup,113 .permissions = mq_permissions,114 .set_ownership = mq_set_ownership,115};116 117bool setup_mq_sysctls(struct ipc_namespace *ns)118{119 struct ctl_table *tbl;120 121 setup_sysctl_set(&ns->mq_set, &set_root, set_is_seen);122 123 tbl = kmemdup(mq_sysctls, sizeof(mq_sysctls), GFP_KERNEL);124 if (tbl) {125 int i;126 127 for (i = 0; i < ARRAY_SIZE(mq_sysctls); i++) {128 if (tbl[i].data == &init_ipc_ns.mq_queues_max)129 tbl[i].data = &ns->mq_queues_max;130 131 else if (tbl[i].data == &init_ipc_ns.mq_msg_max)132 tbl[i].data = &ns->mq_msg_max;133 134 else if (tbl[i].data == &init_ipc_ns.mq_msgsize_max)135 tbl[i].data = &ns->mq_msgsize_max;136 137 else if (tbl[i].data == &init_ipc_ns.mq_msg_default)138 tbl[i].data = &ns->mq_msg_default;139 140 else if (tbl[i].data == &init_ipc_ns.mq_msgsize_default)141 tbl[i].data = &ns->mq_msgsize_default;142 else143 tbl[i].data = NULL;144 }145 146 ns->mq_sysctls = __register_sysctl_table(&ns->mq_set,147 "fs/mqueue", tbl,148 ARRAY_SIZE(mq_sysctls));149 }150 if (!ns->mq_sysctls) {151 kfree(tbl);152 retire_sysctl_set(&ns->mq_set);153 return false;154 }155 156 return true;157}158 159void retire_mq_sysctls(struct ipc_namespace *ns)160{161 const struct ctl_table *tbl;162 163 tbl = ns->mq_sysctls->ctl_table_arg;164 unregister_sysctl_table(ns->mq_sysctls);165 retire_sysctl_set(&ns->mq_set);166 kfree(tbl);167}168