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