diff options
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/msg.c | 162 |
1 files changed, 89 insertions, 73 deletions
@@ -436,10 +436,95 @@ copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version) | |||
436 | } | 436 | } |
437 | } | 437 | } |
438 | 438 | ||
439 | asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | 439 | /* |
440 | * This function handles some msgctl commands which require the rw_mutex | ||
441 | * to be held in write mode. | ||
442 | * NOTE: no locks must be held, the rw_mutex is taken inside this function. | ||
443 | */ | ||
444 | static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | ||
445 | struct msqid_ds __user *buf, int version) | ||
440 | { | 446 | { |
441 | struct kern_ipc_perm *ipcp; | 447 | struct kern_ipc_perm *ipcp; |
442 | struct msq_setbuf uninitialized_var(setbuf); | 448 | struct msq_setbuf setbuf; |
449 | struct msg_queue *msq; | ||
450 | int err; | ||
451 | |||
452 | if (cmd == IPC_SET) { | ||
453 | if (copy_msqid_from_user(&setbuf, buf, version)) | ||
454 | return -EFAULT; | ||
455 | } | ||
456 | |||
457 | down_write(&msg_ids(ns).rw_mutex); | ||
458 | msq = msg_lock_check_down(ns, msqid); | ||
459 | if (IS_ERR(msq)) { | ||
460 | err = PTR_ERR(msq); | ||
461 | goto out_up; | ||
462 | } | ||
463 | |||
464 | ipcp = &msq->q_perm; | ||
465 | |||
466 | err = audit_ipc_obj(ipcp); | ||
467 | if (err) | ||
468 | goto out_unlock; | ||
469 | |||
470 | if (cmd == IPC_SET) { | ||
471 | err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, | ||
472 | setbuf.mode); | ||
473 | if (err) | ||
474 | goto out_unlock; | ||
475 | } | ||
476 | |||
477 | if (current->euid != ipcp->cuid && | ||
478 | current->euid != ipcp->uid && | ||
479 | !capable(CAP_SYS_ADMIN)) { | ||
480 | /* We _could_ check for CAP_CHOWN above, but we don't */ | ||
481 | err = -EPERM; | ||
482 | goto out_unlock; | ||
483 | } | ||
484 | |||
485 | err = security_msg_queue_msgctl(msq, cmd); | ||
486 | if (err) | ||
487 | goto out_unlock; | ||
488 | |||
489 | switch (cmd) { | ||
490 | case IPC_RMID: | ||
491 | freeque(ns, ipcp); | ||
492 | goto out_up; | ||
493 | case IPC_SET: | ||
494 | if (setbuf.qbytes > ns->msg_ctlmnb && | ||
495 | !capable(CAP_SYS_RESOURCE)) { | ||
496 | err = -EPERM; | ||
497 | goto out_unlock; | ||
498 | } | ||
499 | |||
500 | msq->q_qbytes = setbuf.qbytes; | ||
501 | |||
502 | ipcp->uid = setbuf.uid; | ||
503 | ipcp->gid = setbuf.gid; | ||
504 | ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | | ||
505 | (S_IRWXUGO & setbuf.mode); | ||
506 | msq->q_ctime = get_seconds(); | ||
507 | /* sleeping receivers might be excluded by | ||
508 | * stricter permissions. | ||
509 | */ | ||
510 | expunge_all(msq, -EAGAIN); | ||
511 | /* sleeping senders might be able to send | ||
512 | * due to a larger queue size. | ||
513 | */ | ||
514 | ss_wakeup(&msq->q_senders, 0); | ||
515 | break; | ||
516 | default: | ||
517 | err = -EINVAL; | ||
518 | } | ||
519 | out_unlock: | ||
520 | msg_unlock(msq); | ||
521 | out_up: | ||
522 | up_write(&msg_ids(ns).rw_mutex); | ||
523 | return err; | ||
524 | } | ||
525 | |||
526 | asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | ||
527 | { | ||
443 | struct msg_queue *msq; | 528 | struct msg_queue *msq; |
444 | int err, version; | 529 | int err, version; |
445 | struct ipc_namespace *ns; | 530 | struct ipc_namespace *ns; |
@@ -535,82 +620,13 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) | |||
535 | return success_return; | 620 | return success_return; |
536 | } | 621 | } |
537 | case IPC_SET: | 622 | case IPC_SET: |
538 | if (!buf) | ||
539 | return -EFAULT; | ||
540 | if (copy_msqid_from_user(&setbuf, buf, version)) | ||
541 | return -EFAULT; | ||
542 | break; | ||
543 | case IPC_RMID: | 623 | case IPC_RMID: |
544 | break; | 624 | err = msgctl_down(ns, msqid, cmd, buf, version); |
625 | return err; | ||
545 | default: | 626 | default: |
546 | return -EINVAL; | 627 | return -EINVAL; |
547 | } | 628 | } |
548 | 629 | ||
549 | down_write(&msg_ids(ns).rw_mutex); | ||
550 | msq = msg_lock_check_down(ns, msqid); | ||
551 | if (IS_ERR(msq)) { | ||
552 | err = PTR_ERR(msq); | ||
553 | goto out_up; | ||
554 | } | ||
555 | |||
556 | ipcp = &msq->q_perm; | ||
557 | |||
558 | err = audit_ipc_obj(ipcp); | ||
559 | if (err) | ||
560 | goto out_unlock_up; | ||
561 | if (cmd == IPC_SET) { | ||
562 | err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, | ||
563 | setbuf.mode); | ||
564 | if (err) | ||
565 | goto out_unlock_up; | ||
566 | } | ||
567 | |||
568 | err = -EPERM; | ||
569 | if (current->euid != ipcp->cuid && | ||
570 | current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) | ||
571 | /* We _could_ check for CAP_CHOWN above, but we don't */ | ||
572 | goto out_unlock_up; | ||
573 | |||
574 | err = security_msg_queue_msgctl(msq, cmd); | ||
575 | if (err) | ||
576 | goto out_unlock_up; | ||
577 | |||
578 | switch (cmd) { | ||
579 | case IPC_SET: | ||
580 | { | ||
581 | err = -EPERM; | ||
582 | if (setbuf.qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) | ||
583 | goto out_unlock_up; | ||
584 | |||
585 | msq->q_qbytes = setbuf.qbytes; | ||
586 | |||
587 | ipcp->uid = setbuf.uid; | ||
588 | ipcp->gid = setbuf.gid; | ||
589 | ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | | ||
590 | (S_IRWXUGO & setbuf.mode); | ||
591 | msq->q_ctime = get_seconds(); | ||
592 | /* sleeping receivers might be excluded by | ||
593 | * stricter permissions. | ||
594 | */ | ||
595 | expunge_all(msq, -EAGAIN); | ||
596 | /* sleeping senders might be able to send | ||
597 | * due to a larger queue size. | ||
598 | */ | ||
599 | ss_wakeup(&msq->q_senders, 0); | ||
600 | msg_unlock(msq); | ||
601 | break; | ||
602 | } | ||
603 | case IPC_RMID: | ||
604 | freeque(ns, &msq->q_perm); | ||
605 | break; | ||
606 | } | ||
607 | err = 0; | ||
608 | out_up: | ||
609 | up_write(&msg_ids(ns).rw_mutex); | ||
610 | return err; | ||
611 | out_unlock_up: | ||
612 | msg_unlock(msq); | ||
613 | goto out_up; | ||
614 | out_unlock: | 630 | out_unlock: |
615 | msg_unlock(msq); | 631 | msg_unlock(msq); |
616 | return err; | 632 | return err; |