Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a5494dcd EB |
2 | /* |
3 | * Copyright (C) 2007 | |
4 | * | |
5 | * Author: Eric Biederman <ebiederm@xmision.com> | |
a5494dcd EB |
6 | */ |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/ipc.h> | |
10 | #include <linux/nsproxy.h> | |
11 | #include <linux/sysctl.h> | |
12 | #include <linux/uaccess.h> | |
5563cabd | 13 | #include <linux/capability.h> |
ae5e1b22 | 14 | #include <linux/ipc_namespace.h> |
6546bc42 | 15 | #include <linux/msg.h> |
1f5c135e | 16 | #include <linux/slab.h> |
50ec499b | 17 | #include <linux/cred.h> |
6546bc42 | 18 | #include "util.h" |
a5494dcd | 19 | |
1f5c135e | 20 | static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write, |
32927393 | 21 | void *buffer, size_t *lenp, loff_t *ppos) |
b34a6b1d | 22 | { |
dd141a49 AG |
23 | struct ipc_namespace *ns = |
24 | container_of(table->data, struct ipc_namespace, shm_rmid_forced); | |
1f5c135e | 25 | int err; |
b34a6b1d | 26 | |
dd141a49 | 27 | err = proc_dointvec_minmax(table, write, buffer, lenp, ppos); |
b34a6b1d VK |
28 | |
29 | if (err < 0) | |
30 | return err; | |
31 | if (ns->shm_rmid_forced) | |
32 | shm_destroy_orphaned(ns); | |
33 | return err; | |
34 | } | |
35 | ||
0050ee05 | 36 | static int proc_ipc_auto_msgmni(struct ctl_table *table, int write, |
32927393 | 37 | void *buffer, size_t *lenp, loff_t *ppos) |
9eefe520 ND |
38 | { |
39 | struct ctl_table ipc_table; | |
0050ee05 | 40 | int dummy = 0; |
9eefe520 ND |
41 | |
42 | memcpy(&ipc_table, table, sizeof(ipc_table)); | |
0050ee05 MS |
43 | ipc_table.data = &dummy; |
44 | ||
45 | if (write) | |
46 | pr_info_once("writing to auto_msgmni has no effect"); | |
47 | ||
48 | return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); | |
9eefe520 ND |
49 | } |
50 | ||
8c81ddd2 | 51 | static int proc_ipc_sem_dointvec(struct ctl_table *table, int write, |
fff1662c | 52 | void *buffer, size_t *lenp, loff_t *ppos) |
8c81ddd2 | 53 | { |
dd141a49 AG |
54 | struct ipc_namespace *ns = |
55 | container_of(table->data, struct ipc_namespace, sem_ctls); | |
8c81ddd2 | 56 | int ret, semmni; |
1f5c135e | 57 | |
8c81ddd2 | 58 | semmni = ns->sem_ctls[3]; |
1f5c135e | 59 | ret = proc_dointvec(table, write, buffer, lenp, ppos); |
8c81ddd2 WL |
60 | |
61 | if (!ret) | |
def7343f | 62 | ret = sem_check_semmni(ns); |
8c81ddd2 WL |
63 | |
64 | /* | |
65 | * Reset the semmni value if an error happens. | |
66 | */ | |
67 | if (ret) | |
68 | ns->sem_ctls[3] = semmni; | |
69 | return ret; | |
70 | } | |
71 | ||
5ac893b8 WL |
72 | int ipc_mni = IPCMNI; |
73 | int ipc_mni_shift = IPCMNI_SHIFT; | |
99db46ea | 74 | int ipc_min_cycle = RADIX_TREE_MAP_SIZE; |
9eefe520 | 75 | |
1f5c135e | 76 | static struct ctl_table ipc_sysctls[] = { |
a5494dcd | 77 | { |
a5494dcd EB |
78 | .procname = "shmmax", |
79 | .data = &init_ipc_ns.shm_ctlmax, | |
239521f3 | 80 | .maxlen = sizeof(init_ipc_ns.shm_ctlmax), |
a5494dcd | 81 | .mode = 0644, |
1f5c135e | 82 | .proc_handler = proc_doulongvec_minmax, |
a5494dcd EB |
83 | }, |
84 | { | |
a5494dcd EB |
85 | .procname = "shmall", |
86 | .data = &init_ipc_ns.shm_ctlall, | |
239521f3 | 87 | .maxlen = sizeof(init_ipc_ns.shm_ctlall), |
a5494dcd | 88 | .mode = 0644, |
1f5c135e | 89 | .proc_handler = proc_doulongvec_minmax, |
a5494dcd EB |
90 | }, |
91 | { | |
a5494dcd EB |
92 | .procname = "shmmni", |
93 | .data = &init_ipc_ns.shm_ctlmni, | |
239521f3 | 94 | .maxlen = sizeof(init_ipc_ns.shm_ctlmni), |
a5494dcd | 95 | .mode = 0644, |
1f5c135e | 96 | .proc_handler = proc_dointvec_minmax, |
eec4844f | 97 | .extra1 = SYSCTL_ZERO, |
6730e658 | 98 | .extra2 = &ipc_mni, |
a5494dcd | 99 | }, |
b34a6b1d VK |
100 | { |
101 | .procname = "shm_rmid_forced", | |
102 | .data = &init_ipc_ns.shm_rmid_forced, | |
103 | .maxlen = sizeof(init_ipc_ns.shm_rmid_forced), | |
104 | .mode = 0644, | |
105 | .proc_handler = proc_ipc_dointvec_minmax_orphans, | |
dd141a49 AG |
106 | .extra1 = SYSCTL_ZERO, |
107 | .extra2 = SYSCTL_ONE, | |
b34a6b1d | 108 | }, |
a5494dcd | 109 | { |
a5494dcd EB |
110 | .procname = "msgmax", |
111 | .data = &init_ipc_ns.msg_ctlmax, | |
239521f3 | 112 | .maxlen = sizeof(init_ipc_ns.msg_ctlmax), |
a5494dcd | 113 | .mode = 0644, |
1f5c135e | 114 | .proc_handler = proc_dointvec_minmax, |
eec4844f MC |
115 | .extra1 = SYSCTL_ZERO, |
116 | .extra2 = SYSCTL_INT_MAX, | |
a5494dcd EB |
117 | }, |
118 | { | |
a5494dcd EB |
119 | .procname = "msgmni", |
120 | .data = &init_ipc_ns.msg_ctlmni, | |
239521f3 | 121 | .maxlen = sizeof(init_ipc_ns.msg_ctlmni), |
a5494dcd | 122 | .mode = 0644, |
1f5c135e | 123 | .proc_handler = proc_dointvec_minmax, |
eec4844f | 124 | .extra1 = SYSCTL_ZERO, |
6730e658 | 125 | .extra2 = &ipc_mni, |
a5494dcd | 126 | }, |
0050ee05 MS |
127 | { |
128 | .procname = "auto_msgmni", | |
129 | .data = NULL, | |
130 | .maxlen = sizeof(int), | |
131 | .mode = 0644, | |
132 | .proc_handler = proc_ipc_auto_msgmni, | |
eec4844f MC |
133 | .extra1 = SYSCTL_ZERO, |
134 | .extra2 = SYSCTL_ONE, | |
0050ee05 | 135 | }, |
a5494dcd | 136 | { |
a5494dcd EB |
137 | .procname = "msgmnb", |
138 | .data = &init_ipc_ns.msg_ctlmnb, | |
239521f3 | 139 | .maxlen = sizeof(init_ipc_ns.msg_ctlmnb), |
a5494dcd | 140 | .mode = 0644, |
1f5c135e | 141 | .proc_handler = proc_dointvec_minmax, |
eec4844f MC |
142 | .extra1 = SYSCTL_ZERO, |
143 | .extra2 = SYSCTL_INT_MAX, | |
a5494dcd EB |
144 | }, |
145 | { | |
a5494dcd EB |
146 | .procname = "sem", |
147 | .data = &init_ipc_ns.sem_ctls, | |
239521f3 | 148 | .maxlen = 4*sizeof(int), |
a5494dcd | 149 | .mode = 0644, |
8c81ddd2 | 150 | .proc_handler = proc_ipc_sem_dointvec, |
a5494dcd | 151 | }, |
03f59566 SK |
152 | #ifdef CONFIG_CHECKPOINT_RESTORE |
153 | { | |
154 | .procname = "sem_next_id", | |
155 | .data = &init_ipc_ns.ids[IPC_SEM_IDS].next_id, | |
156 | .maxlen = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id), | |
0889f44e AG |
157 | .mode = 0444, |
158 | .proc_handler = proc_dointvec_minmax, | |
159 | .extra1 = SYSCTL_ZERO, | |
160 | .extra2 = SYSCTL_INT_MAX, | |
03f59566 SK |
161 | }, |
162 | { | |
163 | .procname = "msg_next_id", | |
164 | .data = &init_ipc_ns.ids[IPC_MSG_IDS].next_id, | |
165 | .maxlen = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id), | |
0889f44e AG |
166 | .mode = 0444, |
167 | .proc_handler = proc_dointvec_minmax, | |
168 | .extra1 = SYSCTL_ZERO, | |
169 | .extra2 = SYSCTL_INT_MAX, | |
03f59566 SK |
170 | }, |
171 | { | |
172 | .procname = "shm_next_id", | |
173 | .data = &init_ipc_ns.ids[IPC_SHM_IDS].next_id, | |
174 | .maxlen = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id), | |
0889f44e AG |
175 | .mode = 0444, |
176 | .proc_handler = proc_dointvec_minmax, | |
177 | .extra1 = SYSCTL_ZERO, | |
178 | .extra2 = SYSCTL_INT_MAX, | |
03f59566 SK |
179 | }, |
180 | #endif | |
a5494dcd EB |
181 | {} |
182 | }; | |
183 | ||
1f5c135e AG |
184 | static struct ctl_table_set *set_lookup(struct ctl_table_root *root) |
185 | { | |
186 | return ¤t->nsproxy->ipc_ns->ipc_set; | |
187 | } | |
188 | ||
189 | static int set_is_seen(struct ctl_table_set *set) | |
190 | { | |
191 | return ¤t->nsproxy->ipc_ns->ipc_set == set; | |
192 | } | |
193 | ||
50ec499b | 194 | static void ipc_set_ownership(struct ctl_table_header *head, |
50ec499b AG |
195 | kuid_t *uid, kgid_t *gid) |
196 | { | |
197 | struct ipc_namespace *ns = | |
198 | container_of(head->set, struct ipc_namespace, ipc_set); | |
199 | ||
200 | kuid_t ns_root_uid = make_kuid(ns->user_ns, 0); | |
201 | kgid_t ns_root_gid = make_kgid(ns->user_ns, 0); | |
202 | ||
203 | *uid = uid_valid(ns_root_uid) ? ns_root_uid : GLOBAL_ROOT_UID; | |
204 | *gid = gid_valid(ns_root_gid) ? ns_root_gid : GLOBAL_ROOT_GID; | |
205 | } | |
206 | ||
795f90c6 | 207 | static int ipc_permissions(struct ctl_table_header *head, const struct ctl_table *table) |
0889f44e AG |
208 | { |
209 | int mode = table->mode; | |
210 | ||
211 | #ifdef CONFIG_CHECKPOINT_RESTORE | |
50ec499b AG |
212 | struct ipc_namespace *ns = |
213 | container_of(head->set, struct ipc_namespace, ipc_set); | |
0889f44e AG |
214 | |
215 | if (((table->data == &ns->ids[IPC_SEM_IDS].next_id) || | |
216 | (table->data == &ns->ids[IPC_MSG_IDS].next_id) || | |
217 | (table->data == &ns->ids[IPC_SHM_IDS].next_id)) && | |
218 | checkpoint_restore_ns_capable(ns->user_ns)) | |
219 | mode = 0666; | |
50ec499b | 220 | else |
0889f44e | 221 | #endif |
50ec499b AG |
222 | { |
223 | kuid_t ns_root_uid; | |
224 | kgid_t ns_root_gid; | |
225 | ||
520713a9 | 226 | ipc_set_ownership(head, &ns_root_uid, &ns_root_gid); |
50ec499b AG |
227 | |
228 | if (uid_eq(current_euid(), ns_root_uid)) | |
229 | mode >>= 6; | |
230 | ||
231 | else if (in_egroup_p(ns_root_gid)) | |
232 | mode >>= 3; | |
233 | } | |
234 | ||
235 | mode &= 7; | |
236 | ||
237 | return (mode << 6) | (mode << 3) | mode; | |
0889f44e AG |
238 | } |
239 | ||
1f5c135e AG |
240 | static struct ctl_table_root set_root = { |
241 | .lookup = set_lookup, | |
0889f44e | 242 | .permissions = ipc_permissions, |
50ec499b | 243 | .set_ownership = ipc_set_ownership, |
a5494dcd EB |
244 | }; |
245 | ||
1f5c135e AG |
246 | bool setup_ipc_sysctls(struct ipc_namespace *ns) |
247 | { | |
248 | struct ctl_table *tbl; | |
249 | ||
250 | setup_sysctl_set(&ns->ipc_set, &set_root, set_is_seen); | |
251 | ||
252 | tbl = kmemdup(ipc_sysctls, sizeof(ipc_sysctls), GFP_KERNEL); | |
253 | if (tbl) { | |
254 | int i; | |
255 | ||
256 | for (i = 0; i < ARRAY_SIZE(ipc_sysctls); i++) { | |
38cd5b12 | 257 | if (tbl[i].data == &init_ipc_ns.shm_ctlmax) |
1f5c135e AG |
258 | tbl[i].data = &ns->shm_ctlmax; |
259 | ||
38cd5b12 | 260 | else if (tbl[i].data == &init_ipc_ns.shm_ctlall) |
1f5c135e AG |
261 | tbl[i].data = &ns->shm_ctlall; |
262 | ||
38cd5b12 | 263 | else if (tbl[i].data == &init_ipc_ns.shm_ctlmni) |
1f5c135e AG |
264 | tbl[i].data = &ns->shm_ctlmni; |
265 | ||
38cd5b12 | 266 | else if (tbl[i].data == &init_ipc_ns.shm_rmid_forced) |
1f5c135e | 267 | tbl[i].data = &ns->shm_rmid_forced; |
1f5c135e | 268 | |
38cd5b12 | 269 | else if (tbl[i].data == &init_ipc_ns.msg_ctlmax) |
1f5c135e AG |
270 | tbl[i].data = &ns->msg_ctlmax; |
271 | ||
38cd5b12 | 272 | else if (tbl[i].data == &init_ipc_ns.msg_ctlmni) |
1f5c135e AG |
273 | tbl[i].data = &ns->msg_ctlmni; |
274 | ||
38cd5b12 | 275 | else if (tbl[i].data == &init_ipc_ns.msg_ctlmnb) |
1f5c135e AG |
276 | tbl[i].data = &ns->msg_ctlmnb; |
277 | ||
38cd5b12 | 278 | else if (tbl[i].data == &init_ipc_ns.sem_ctls) |
1f5c135e | 279 | tbl[i].data = &ns->sem_ctls; |
1f5c135e | 280 | #ifdef CONFIG_CHECKPOINT_RESTORE |
38cd5b12 | 281 | else if (tbl[i].data == &init_ipc_ns.ids[IPC_SEM_IDS].next_id) |
1f5c135e | 282 | tbl[i].data = &ns->ids[IPC_SEM_IDS].next_id; |
1f5c135e | 283 | |
38cd5b12 | 284 | else if (tbl[i].data == &init_ipc_ns.ids[IPC_MSG_IDS].next_id) |
1f5c135e | 285 | tbl[i].data = &ns->ids[IPC_MSG_IDS].next_id; |
1f5c135e | 286 | |
38cd5b12 | 287 | else if (tbl[i].data == &init_ipc_ns.ids[IPC_SHM_IDS].next_id) |
1f5c135e | 288 | tbl[i].data = &ns->ids[IPC_SHM_IDS].next_id; |
1f5c135e | 289 | #endif |
38cd5b12 | 290 | else |
1f5c135e | 291 | tbl[i].data = NULL; |
1f5c135e AG |
292 | } |
293 | ||
8e882910 | 294 | ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl, |
bff97cf1 | 295 | ARRAY_SIZE(ipc_sysctls)); |
1f5c135e AG |
296 | } |
297 | if (!ns->ipc_sysctls) { | |
298 | kfree(tbl); | |
299 | retire_sysctl_set(&ns->ipc_set); | |
300 | return false; | |
301 | } | |
302 | ||
303 | return true; | |
304 | } | |
305 | ||
306 | void retire_ipc_sysctls(struct ipc_namespace *ns) | |
307 | { | |
bfa858f2 | 308 | const struct ctl_table *tbl; |
1f5c135e AG |
309 | |
310 | tbl = ns->ipc_sysctls->ctl_table_arg; | |
311 | unregister_sysctl_table(ns->ipc_sysctls); | |
312 | retire_sysctl_set(&ns->ipc_set); | |
313 | kfree(tbl); | |
314 | } | |
315 | ||
a5494dcd EB |
316 | static int __init ipc_sysctl_init(void) |
317 | { | |
1f5c135e AG |
318 | if (!setup_ipc_sysctls(&init_ipc_ns)) { |
319 | pr_warn("ipc sysctl registration failed\n"); | |
320 | return -ENOMEM; | |
321 | } | |
a5494dcd EB |
322 | return 0; |
323 | } | |
324 | ||
6d08a256 | 325 | device_initcall(ipc_sysctl_init); |
5ac893b8 WL |
326 | |
327 | static int __init ipc_mni_extend(char *str) | |
328 | { | |
329 | ipc_mni = IPCMNI_EXTEND; | |
330 | ipc_mni_shift = IPCMNI_EXTEND_SHIFT; | |
99db46ea | 331 | ipc_min_cycle = IPCMNI_EXTEND_MIN_CYCLE; |
5ac893b8 WL |
332 | pr_info("IPCMNI extended to %d.\n", ipc_mni); |
333 | return 0; | |
334 | } | |
335 | early_param("ipcmni_extend", ipc_mni_extend); |