1 | /* $NetBSD: sysv_ipc.c,v 1.32 2015/12/05 00:51:42 pgoyette Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 2007 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Charles M. Hannum. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: sysv_ipc.c,v 1.32 2015/12/05 00:51:42 pgoyette Exp $" ); |
34 | |
35 | #ifdef _KERNEL_OPT |
36 | #include "opt_sysv.h" |
37 | #include "opt_compat_netbsd.h" |
38 | #endif |
39 | |
40 | #include <sys/syscall.h> |
41 | #include <sys/syscallargs.h> |
42 | #include <sys/syscallvar.h> |
43 | #include <sys/param.h> |
44 | #include <sys/kernel.h> |
45 | #include <sys/proc.h> |
46 | #include <sys/ipc.h> |
47 | #ifdef SYSVMSG |
48 | #include <sys/msg.h> |
49 | #endif |
50 | #ifdef SYSVSEM |
51 | #include <sys/sem.h> |
52 | #endif |
53 | #ifdef SYSVSHM |
54 | #include <sys/shm.h> |
55 | #endif |
56 | #include <sys/systm.h> |
57 | #include <sys/kmem.h> |
58 | #include <sys/module.h> |
59 | #include <sys/mount.h> |
60 | #include <sys/vnode.h> |
61 | #include <sys/stat.h> |
62 | #include <sys/sysctl.h> |
63 | #include <sys/kauth.h> |
64 | |
65 | /* |
66 | * Values in support of System V compatible shared memory. XXX |
67 | * (originally located in sys/conf/param.c) |
68 | */ |
69 | #ifdef SYSVSHM |
70 | #if !defined(SHMMAX) && defined(SHMMAXPGS) |
71 | #define SHMMAX SHMMAXPGS /* shminit() performs a `*= PAGE_SIZE' */ |
72 | #elif !defined(SHMMAX) |
73 | #define SHMMAX 0 |
74 | #endif |
75 | #ifndef SHMMIN |
76 | #define SHMMIN 1 |
77 | #endif |
78 | #ifndef SHMMNI |
79 | #define SHMMNI 128 /* <64k, see IPCID_TO_IX in ipc.h */ |
80 | #endif |
81 | #ifndef SHMSEG |
82 | #define SHMSEG 128 |
83 | #endif |
84 | |
85 | struct shminfo shminfo = { |
86 | SHMMAX, |
87 | SHMMIN, |
88 | SHMMNI, |
89 | SHMSEG, |
90 | 0 |
91 | }; |
92 | #endif |
93 | |
94 | /* |
95 | * Values in support of System V compatible semaphores. |
96 | */ |
97 | #ifdef SYSVSEM |
98 | struct seminfo seminfo = { |
99 | SEMMAP, /* # of entries in semaphore map */ |
100 | SEMMNI, /* # of semaphore identifiers */ |
101 | SEMMNS, /* # of semaphores in system */ |
102 | SEMMNU, /* # of undo structures in system */ |
103 | SEMMSL, /* max # of semaphores per id */ |
104 | SEMOPM, /* max # of operations per semop call */ |
105 | SEMUME, /* max # of undo entries per process */ |
106 | SEMUSZ, /* size in bytes of undo structure */ |
107 | SEMVMX, /* semaphore maximum value */ |
108 | SEMAEM /* adjust on exit max value */ |
109 | }; |
110 | #endif |
111 | |
112 | /* |
113 | * Values in support of System V compatible messages. |
114 | */ |
115 | #ifdef SYSVMSG |
116 | struct msginfo msginfo = { |
117 | MSGMAX, /* max chars in a message */ |
118 | MSGMNI, /* # of message queue identifiers */ |
119 | MSGMNB, /* max chars in a queue */ |
120 | MSGTQL, /* max messages in system */ |
121 | MSGSSZ, /* size of a message segment */ |
122 | /* (must be small power of 2 greater than 4) */ |
123 | MSGSEG /* number of message segments */ |
124 | }; |
125 | #endif |
126 | |
127 | #if defined(COMPAT_50) |
128 | int sysctl_kern_sysvipc50(SYSCTLFN_PROTO); |
129 | #endif |
130 | |
131 | MODULE(MODULE_CLASS_EXEC, sysv_ipc, NULL); |
132 | |
133 | SYSCTL_SETUP_PROTO(sysctl_ipc_setup); |
134 | |
135 | static struct sysctllog *sysctl_sysvipc_clog = NULL; |
136 | |
137 | static const struct syscall_package sysvipc_syscalls[] = { |
138 | #if defined(SYSVSHM) |
139 | { SYS___shmctl50, 0, (sy_call_t *)sys___shmctl50 }, |
140 | { SYS_shmat, 0, (sy_call_t *)sys_shmat }, |
141 | { SYS_shmdt, 0, (sy_call_t *)sys_shmdt }, |
142 | { SYS_shmget, 0, (sy_call_t *)sys_shmget }, |
143 | #if defined(COMPAT_10) && !defined(_LP64) |
144 | { SYS_compat_10_oshmsys, 0, (sy_call_t *)compat_10_sys_shmsys }, |
145 | #endif |
146 | #if defined(COMPAT_14) |
147 | { SYS_compat_14_shmctl, 0, (sy_call_t *)compat_14_sys_shmctl }, |
148 | #endif |
149 | #if defined(COMPAT_50) |
150 | { SYS_compat_50___shmctl13, 0, (sy_call_t *)compat_50_sys___shmctl13 }, |
151 | #endif |
152 | #endif /* SYSVSHM */ |
153 | |
154 | #if defined(SYSVSEM) |
155 | { SYS_____semctl50, 0, (sy_call_t *)sys_____semctl50 }, |
156 | { SYS_semget, 0, (sy_call_t *)sys_semget }, |
157 | { SYS_semop, 0, (sy_call_t *)sys_semop }, |
158 | { SYS_semconfig, 0, (sy_call_t *)sys_semconfig }, |
159 | #if defined(COMPAT_10) && !defined(_LP64) |
160 | { SYS_compat_10_osemsys, 0, (sy_call_t *)compat_10_sys_semsys }, |
161 | #endif |
162 | #if defined(COMPAT_14) |
163 | { SYS_compat_14___semctl, 0, (sy_call_t *)compat_14_sys___semctl }, |
164 | #endif |
165 | #if defined(COMPAT_50) |
166 | { SYS_compat_50_____semctl13, 0, (sy_call_t *)compat_50_sys_____semctl13 }, |
167 | #endif |
168 | #endif /* SYSVSEM */ |
169 | |
170 | #if defined(SYSVMSG) |
171 | { SYS___msgctl50, 0, (sy_call_t *)sys___msgctl50 }, |
172 | { SYS_msgget, 0, (sy_call_t *)sys_msgget }, |
173 | { SYS_msgsnd, 0, (sy_call_t *)sys_msgsnd }, |
174 | { SYS_msgrcv, 0, (sy_call_t *)sys_msgrcv }, |
175 | #if defined(COMPAT_10) && !defined(_LP64) |
176 | { SYS_compat_10_omsgsys, 0, (sy_call_t *)compat_10_sys_msgsys }, |
177 | #endif |
178 | #if defined(COMPAT_14) |
179 | { SYS_compat_14_msgctl, 0, (sy_call_t *)compat_14_sys_msgctl }, |
180 | #endif |
181 | #if defined(COMPAT_50) |
182 | { SYS_compat_50___msgctl13, 0, (sy_call_t *)compat_50_sys___msgctl13 }, |
183 | #endif |
184 | #endif /* SYSVMSG */ |
185 | { 0, 0, NULL } |
186 | }; |
187 | |
188 | static int |
189 | sysv_ipc_modcmd(modcmd_t cmd, void *arg) |
190 | { |
191 | int error = 0; |
192 | |
193 | switch (cmd) { |
194 | case MODULE_CMD_INIT: |
195 | /* Set up the kauth listener */ |
196 | sysvipcinit(); |
197 | |
198 | #ifdef _MODULE |
199 | /* Set up the common sysctl tree */ |
200 | sysctl_ipc_setup(&sysctl_sysvipc_clog); |
201 | #endif |
202 | |
203 | /* Link the system calls */ |
204 | error = syscall_establish(NULL, sysvipc_syscalls); |
205 | if (error) |
206 | sysvipcfini(); |
207 | |
208 | /* |
209 | * Initialize each sub-component, including their |
210 | * sysctl data |
211 | */ |
212 | #ifdef SYSVSHM |
213 | shminit(&sysctl_sysvipc_clog); |
214 | #endif |
215 | #ifdef SYSVSEM |
216 | seminit(&sysctl_sysvipc_clog); |
217 | #endif |
218 | #ifdef SYSVMSG |
219 | msginit(&sysctl_sysvipc_clog); |
220 | #endif |
221 | break; |
222 | case MODULE_CMD_FINI: |
223 | /* |
224 | * Make sure no subcomponents are active. Each one |
225 | * tells us if it is busy, and if it was _not_ busy, |
226 | * we assume it has already done its own clean-up. |
227 | * So we might need to re-init any components that |
228 | * are successfully fini'd if we find one that is |
229 | * still busy. |
230 | */ |
231 | #ifdef SYSVSHM |
232 | if (shmfini()) { |
233 | return EBUSY; |
234 | } |
235 | #endif |
236 | #ifdef SYSVSEM |
237 | if (semfini()) { |
238 | #ifdef SYSVSHM |
239 | shminit(NULL); |
240 | #endif |
241 | return EBUSY; |
242 | } |
243 | #endif |
244 | #ifdef SYSVMSG |
245 | if (msgfini()) { |
246 | #ifdef SYSVSEM |
247 | seminit(NULL); |
248 | #endif |
249 | #ifdef SYSVSHM |
250 | shminit(NULL); |
251 | #endif |
252 | return EBUSY; |
253 | } |
254 | #endif |
255 | |
256 | /* Unlink the system calls. */ |
257 | error = syscall_disestablish(NULL, sysvipc_syscalls); |
258 | if (error) |
259 | return error; |
260 | |
261 | #ifdef _MODULE |
262 | /* Remove the sysctl sub-trees */ |
263 | sysctl_teardown(&sysctl_sysvipc_clog); |
264 | #endif |
265 | |
266 | /* Remove the kauth listener */ |
267 | sysvipcfini(); |
268 | break; |
269 | default: |
270 | return ENOTTY; |
271 | } |
272 | return error; |
273 | } |
274 | |
275 | static kauth_listener_t sysvipc_listener = NULL; |
276 | |
277 | static int |
278 | sysvipc_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, |
279 | void *arg0, void *arg1, void *arg2, void *arg3) |
280 | { |
281 | mode_t mask; |
282 | int ismember = 0; |
283 | struct ipc_perm *perm; |
284 | int mode; |
285 | enum kauth_system_req req; |
286 | |
287 | req = (enum kauth_system_req)arg0; |
288 | |
289 | if (!(action == KAUTH_SYSTEM_SYSVIPC && |
290 | req == KAUTH_REQ_SYSTEM_SYSVIPC_BYPASS)) |
291 | return KAUTH_RESULT_DEFER; |
292 | |
293 | perm = arg1; |
294 | mode = (int)(uintptr_t)arg2; |
295 | |
296 | if (mode == IPC_M) { |
297 | if (kauth_cred_geteuid(cred) == perm->uid || |
298 | kauth_cred_geteuid(cred) == perm->cuid) |
299 | return (KAUTH_RESULT_ALLOW); |
300 | return (KAUTH_RESULT_DEFER); /* EPERM */ |
301 | } |
302 | |
303 | mask = 0; |
304 | |
305 | if (kauth_cred_geteuid(cred) == perm->uid || |
306 | kauth_cred_geteuid(cred) == perm->cuid) { |
307 | if (mode & IPC_R) |
308 | mask |= S_IRUSR; |
309 | if (mode & IPC_W) |
310 | mask |= S_IWUSR; |
311 | return ((perm->mode & mask) == mask ? KAUTH_RESULT_ALLOW : KAUTH_RESULT_DEFER /* EACCES */); |
312 | } |
313 | |
314 | if (kauth_cred_getegid(cred) == perm->gid || |
315 | (kauth_cred_ismember_gid(cred, perm->gid, &ismember) == 0 && ismember) || |
316 | kauth_cred_getegid(cred) == perm->cgid || |
317 | (kauth_cred_ismember_gid(cred, perm->cgid, &ismember) == 0 && ismember)) { |
318 | if (mode & IPC_R) |
319 | mask |= S_IRGRP; |
320 | if (mode & IPC_W) |
321 | mask |= S_IWGRP; |
322 | return ((perm->mode & mask) == mask ? KAUTH_RESULT_ALLOW : KAUTH_RESULT_DEFER /* EACCES */); |
323 | } |
324 | |
325 | if (mode & IPC_R) |
326 | mask |= S_IROTH; |
327 | if (mode & IPC_W) |
328 | mask |= S_IWOTH; |
329 | return ((perm->mode & mask) == mask ? KAUTH_RESULT_ALLOW : KAUTH_RESULT_DEFER /* EACCES */); |
330 | } |
331 | |
332 | /* |
333 | * Check for ipc permission |
334 | */ |
335 | |
336 | int |
337 | ipcperm(kauth_cred_t cred, struct ipc_perm *perm, int mode) |
338 | { |
339 | int error; |
340 | |
341 | error = kauth_authorize_system(cred, KAUTH_SYSTEM_SYSVIPC, |
342 | KAUTH_REQ_SYSTEM_SYSVIPC_BYPASS, perm, KAUTH_ARG(mode), NULL); |
343 | if (error == 0) |
344 | return (0); |
345 | |
346 | /* Adjust EPERM and EACCES errors until there's a better way to do this. */ |
347 | if (mode != IPC_M) |
348 | error = EACCES; |
349 | |
350 | return error; |
351 | } |
352 | |
353 | void |
354 | sysvipcfini(void) |
355 | { |
356 | |
357 | KASSERT(sysvipc_listener != NULL); |
358 | kauth_unlisten_scope(sysvipc_listener); |
359 | sysvipc_listener = NULL; |
360 | } |
361 | |
362 | void |
363 | sysvipcinit(void) |
364 | { |
365 | |
366 | KASSERT(sysvipc_listener == NULL); |
367 | |
368 | sysvipc_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, |
369 | sysvipc_listener_cb, NULL); |
370 | } |
371 | |
372 | static int |
373 | sysctl_kern_sysvipc(SYSCTLFN_ARGS) |
374 | { |
375 | void *where = oldp; |
376 | size_t sz, *sizep = oldlenp; |
377 | #ifdef SYSVMSG |
378 | struct msg_sysctl_info *msgsi = NULL; |
379 | #endif |
380 | #ifdef SYSVSEM |
381 | struct sem_sysctl_info *semsi = NULL; |
382 | #endif |
383 | #ifdef SYSVSHM |
384 | struct shm_sysctl_info *shmsi = NULL; |
385 | #endif |
386 | size_t infosize, dssize, tsize, buflen; |
387 | void *bf = NULL; |
388 | char *start; |
389 | int32_t nds; |
390 | int i, error, ret; |
391 | |
392 | /* |
393 | * If present, call the compat sysctl() code. If it handles the request |
394 | * completely (either success or error), return. Otherwise fallthrough |
395 | * to the non-compat sysctl code. |
396 | */ |
397 | |
398 | #if defined(COMPAT_50) |
399 | error = sysctl_kern_sysvipc50(SYSCTLFN_CALL(rnode)); |
400 | if (error != EPASSTHROUGH) |
401 | return error; |
402 | #endif |
403 | |
404 | if (namelen != 1) |
405 | return EINVAL; |
406 | |
407 | start = where; |
408 | buflen = *sizep; |
409 | |
410 | switch (*name) { |
411 | case KERN_SYSVIPC_MSG_INFO: |
412 | #ifdef SYSVMSG |
413 | infosize = sizeof(msgsi->msginfo); |
414 | nds = msginfo.msgmni; |
415 | dssize = sizeof(msgsi->msgids[0]); |
416 | break; |
417 | #else |
418 | return EINVAL; |
419 | #endif |
420 | case KERN_SYSVIPC_SEM_INFO: |
421 | #ifdef SYSVSEM |
422 | infosize = sizeof(semsi->seminfo); |
423 | nds = seminfo.semmni; |
424 | dssize = sizeof(semsi->semids[0]); |
425 | break; |
426 | #else |
427 | return EINVAL; |
428 | #endif |
429 | case KERN_SYSVIPC_SHM_INFO: |
430 | #ifdef SYSVSHM |
431 | infosize = sizeof(shmsi->shminfo); |
432 | nds = shminfo.shmmni; |
433 | dssize = sizeof(shmsi->shmids[0]); |
434 | break; |
435 | #else |
436 | return EINVAL; |
437 | #endif |
438 | default: |
439 | return EINVAL; |
440 | } |
441 | /* |
442 | * Round infosize to 64 bit boundary if requesting more than just |
443 | * the info structure or getting the total data size. |
444 | */ |
445 | if (where == NULL || *sizep > infosize) |
446 | infosize = roundup(infosize, sizeof(quad_t)); |
447 | tsize = infosize + nds * dssize; |
448 | |
449 | /* Return just the total size required. */ |
450 | if (where == NULL) { |
451 | *sizep = tsize; |
452 | return 0; |
453 | } |
454 | |
455 | /* Not enough room for even the info struct. */ |
456 | if (buflen < infosize) { |
457 | *sizep = 0; |
458 | return ENOMEM; |
459 | } |
460 | sz = min(tsize, buflen); |
461 | bf = kmem_zalloc(sz, KM_SLEEP); |
462 | |
463 | switch (*name) { |
464 | #ifdef SYSVMSG |
465 | case KERN_SYSVIPC_MSG_INFO: |
466 | msgsi = (struct msg_sysctl_info *)bf; |
467 | msgsi->msginfo = msginfo; |
468 | break; |
469 | #endif |
470 | #ifdef SYSVSEM |
471 | case KERN_SYSVIPC_SEM_INFO: |
472 | semsi = (struct sem_sysctl_info *)bf; |
473 | semsi->seminfo = seminfo; |
474 | break; |
475 | #endif |
476 | #ifdef SYSVSHM |
477 | case KERN_SYSVIPC_SHM_INFO: |
478 | shmsi = (struct shm_sysctl_info *)bf; |
479 | shmsi->shminfo = shminfo; |
480 | break; |
481 | #endif |
482 | } |
483 | buflen -= infosize; |
484 | |
485 | ret = 0; |
486 | if (buflen > 0) { |
487 | /* Fill in the IPC data structures. */ |
488 | for (i = 0; i < nds; i++) { |
489 | if (buflen < dssize) { |
490 | ret = ENOMEM; |
491 | break; |
492 | } |
493 | switch (*name) { |
494 | #ifdef SYSVMSG |
495 | case KERN_SYSVIPC_MSG_INFO: |
496 | mutex_enter(&msgmutex); |
497 | SYSCTL_FILL_MSG(msqs[i].msq_u, msgsi->msgids[i]); |
498 | mutex_exit(&msgmutex); |
499 | break; |
500 | #endif |
501 | #ifdef SYSVSEM |
502 | case KERN_SYSVIPC_SEM_INFO: |
503 | SYSCTL_FILL_SEM(sema[i], semsi->semids[i]); |
504 | break; |
505 | #endif |
506 | #ifdef SYSVSHM |
507 | case KERN_SYSVIPC_SHM_INFO: |
508 | SYSCTL_FILL_SHM(shmsegs[i], shmsi->shmids[i]); |
509 | break; |
510 | #endif |
511 | } |
512 | buflen -= dssize; |
513 | } |
514 | } |
515 | *sizep -= buflen; |
516 | error = copyout(bf, start, *sizep); |
517 | /* If copyout succeeded, use return code set earlier. */ |
518 | if (error == 0) |
519 | error = ret; |
520 | if (bf) |
521 | kmem_free(bf, sz); |
522 | return error; |
523 | } |
524 | |
525 | SYSCTL_SETUP(sysctl_ipc_setup, "sysctl kern.ipc subtree setup" ) |
526 | { |
527 | |
528 | sysctl_createv(clog, 0, NULL, NULL, |
529 | CTLFLAG_PERMANENT, |
530 | CTLTYPE_NODE, "ipc" , |
531 | SYSCTL_DESCR("SysV IPC options" ), |
532 | NULL, 0, NULL, 0, |
533 | CTL_KERN, KERN_SYSVIPC, CTL_EOL); |
534 | |
535 | sysctl_createv(clog, 0, NULL, NULL, |
536 | CTLFLAG_PERMANENT, |
537 | CTLTYPE_STRUCT, "sysvipc_info" , |
538 | SYSCTL_DESCR("System V style IPC information" ), |
539 | sysctl_kern_sysvipc, 0, NULL, 0, |
540 | CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_INFO, CTL_EOL); |
541 | } |
542 | |