diff options
Diffstat (limited to 'kernel/sys.c')
-rw-r--r-- | kernel/sys.c | 586 |
1 files changed, 332 insertions, 254 deletions
diff --git a/kernel/sys.c b/kernel/sys.c index 5fc3a0cfb994..d356d79e84ac 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -112,12 +112,17 @@ EXPORT_SYMBOL(cad_pid); | |||
112 | 112 | ||
113 | void (*pm_power_off_prepare)(void); | 113 | void (*pm_power_off_prepare)(void); |
114 | 114 | ||
115 | /* | ||
116 | * set the priority of a task | ||
117 | * - the caller must hold the RCU read lock | ||
118 | */ | ||
115 | static int set_one_prio(struct task_struct *p, int niceval, int error) | 119 | static int set_one_prio(struct task_struct *p, int niceval, int error) |
116 | { | 120 | { |
121 | const struct cred *cred = current_cred(), *pcred = __task_cred(p); | ||
117 | int no_nice; | 122 | int no_nice; |
118 | 123 | ||
119 | if (p->uid != current->euid && | 124 | if (pcred->uid != cred->euid && |
120 | p->euid != current->euid && !capable(CAP_SYS_NICE)) { | 125 | pcred->euid != cred->euid && !capable(CAP_SYS_NICE)) { |
121 | error = -EPERM; | 126 | error = -EPERM; |
122 | goto out; | 127 | goto out; |
123 | } | 128 | } |
@@ -141,6 +146,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval) | |||
141 | { | 146 | { |
142 | struct task_struct *g, *p; | 147 | struct task_struct *g, *p; |
143 | struct user_struct *user; | 148 | struct user_struct *user; |
149 | const struct cred *cred = current_cred(); | ||
144 | int error = -EINVAL; | 150 | int error = -EINVAL; |
145 | struct pid *pgrp; | 151 | struct pid *pgrp; |
146 | 152 | ||
@@ -174,18 +180,18 @@ asmlinkage long sys_setpriority(int which, int who, int niceval) | |||
174 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 180 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
175 | break; | 181 | break; |
176 | case PRIO_USER: | 182 | case PRIO_USER: |
177 | user = current->user; | 183 | user = (struct user_struct *) cred->user; |
178 | if (!who) | 184 | if (!who) |
179 | who = current->uid; | 185 | who = cred->uid; |
180 | else | 186 | else if ((who != cred->uid) && |
181 | if ((who != current->uid) && !(user = find_user(who))) | 187 | !(user = find_user(who))) |
182 | goto out_unlock; /* No processes for this user */ | 188 | goto out_unlock; /* No processes for this user */ |
183 | 189 | ||
184 | do_each_thread(g, p) | 190 | do_each_thread(g, p) |
185 | if (p->uid == who) | 191 | if (__task_cred(p)->uid == who) |
186 | error = set_one_prio(p, niceval, error); | 192 | error = set_one_prio(p, niceval, error); |
187 | while_each_thread(g, p); | 193 | while_each_thread(g, p); |
188 | if (who != current->uid) | 194 | if (who != cred->uid) |
189 | free_uid(user); /* For find_user() */ | 195 | free_uid(user); /* For find_user() */ |
190 | break; | 196 | break; |
191 | } | 197 | } |
@@ -205,6 +211,7 @@ asmlinkage long sys_getpriority(int which, int who) | |||
205 | { | 211 | { |
206 | struct task_struct *g, *p; | 212 | struct task_struct *g, *p; |
207 | struct user_struct *user; | 213 | struct user_struct *user; |
214 | const struct cred *cred = current_cred(); | ||
208 | long niceval, retval = -ESRCH; | 215 | long niceval, retval = -ESRCH; |
209 | struct pid *pgrp; | 216 | struct pid *pgrp; |
210 | 217 | ||
@@ -236,21 +243,21 @@ asmlinkage long sys_getpriority(int which, int who) | |||
236 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 243 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
237 | break; | 244 | break; |
238 | case PRIO_USER: | 245 | case PRIO_USER: |
239 | user = current->user; | 246 | user = (struct user_struct *) cred->user; |
240 | if (!who) | 247 | if (!who) |
241 | who = current->uid; | 248 | who = cred->uid; |
242 | else | 249 | else if ((who != cred->uid) && |
243 | if ((who != current->uid) && !(user = find_user(who))) | 250 | !(user = find_user(who))) |
244 | goto out_unlock; /* No processes for this user */ | 251 | goto out_unlock; /* No processes for this user */ |
245 | 252 | ||
246 | do_each_thread(g, p) | 253 | do_each_thread(g, p) |
247 | if (p->uid == who) { | 254 | if (__task_cred(p)->uid == who) { |
248 | niceval = 20 - task_nice(p); | 255 | niceval = 20 - task_nice(p); |
249 | if (niceval > retval) | 256 | if (niceval > retval) |
250 | retval = niceval; | 257 | retval = niceval; |
251 | } | 258 | } |
252 | while_each_thread(g, p); | 259 | while_each_thread(g, p); |
253 | if (who != current->uid) | 260 | if (who != cred->uid) |
254 | free_uid(user); /* for find_user() */ | 261 | free_uid(user); /* for find_user() */ |
255 | break; | 262 | break; |
256 | } | 263 | } |
@@ -472,46 +479,48 @@ void ctrl_alt_del(void) | |||
472 | */ | 479 | */ |
473 | asmlinkage long sys_setregid(gid_t rgid, gid_t egid) | 480 | asmlinkage long sys_setregid(gid_t rgid, gid_t egid) |
474 | { | 481 | { |
475 | int old_rgid = current->gid; | 482 | const struct cred *old; |
476 | int old_egid = current->egid; | 483 | struct cred *new; |
477 | int new_rgid = old_rgid; | ||
478 | int new_egid = old_egid; | ||
479 | int retval; | 484 | int retval; |
480 | 485 | ||
486 | new = prepare_creds(); | ||
487 | if (!new) | ||
488 | return -ENOMEM; | ||
489 | old = current_cred(); | ||
490 | |||
481 | retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE); | 491 | retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE); |
482 | if (retval) | 492 | if (retval) |
483 | return retval; | 493 | goto error; |
484 | 494 | ||
495 | retval = -EPERM; | ||
485 | if (rgid != (gid_t) -1) { | 496 | if (rgid != (gid_t) -1) { |
486 | if ((old_rgid == rgid) || | 497 | if (old->gid == rgid || |
487 | (current->egid==rgid) || | 498 | old->egid == rgid || |
488 | capable(CAP_SETGID)) | 499 | capable(CAP_SETGID)) |
489 | new_rgid = rgid; | 500 | new->gid = rgid; |
490 | else | 501 | else |
491 | return -EPERM; | 502 | goto error; |
492 | } | 503 | } |
493 | if (egid != (gid_t) -1) { | 504 | if (egid != (gid_t) -1) { |
494 | if ((old_rgid == egid) || | 505 | if (old->gid == egid || |
495 | (current->egid == egid) || | 506 | old->egid == egid || |
496 | (current->sgid == egid) || | 507 | old->sgid == egid || |
497 | capable(CAP_SETGID)) | 508 | capable(CAP_SETGID)) |
498 | new_egid = egid; | 509 | new->egid = egid; |
499 | else | 510 | else |
500 | return -EPERM; | 511 | goto error; |
501 | } | ||
502 | if (new_egid != old_egid) { | ||
503 | set_dumpable(current->mm, suid_dumpable); | ||
504 | smp_wmb(); | ||
505 | } | 512 | } |
513 | |||
506 | if (rgid != (gid_t) -1 || | 514 | if (rgid != (gid_t) -1 || |
507 | (egid != (gid_t) -1 && egid != old_rgid)) | 515 | (egid != (gid_t) -1 && egid != old->gid)) |
508 | current->sgid = new_egid; | 516 | new->sgid = new->egid; |
509 | current->fsgid = new_egid; | 517 | new->fsgid = new->egid; |
510 | current->egid = new_egid; | 518 | |
511 | current->gid = new_rgid; | 519 | return commit_creds(new); |
512 | key_fsgid_changed(current); | 520 | |
513 | proc_id_connector(current, PROC_EVENT_GID); | 521 | error: |
514 | return 0; | 522 | abort_creds(new); |
523 | return retval; | ||
515 | } | 524 | } |
516 | 525 | ||
517 | /* | 526 | /* |
@@ -521,56 +530,54 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid) | |||
521 | */ | 530 | */ |
522 | asmlinkage long sys_setgid(gid_t gid) | 531 | asmlinkage long sys_setgid(gid_t gid) |
523 | { | 532 | { |
524 | int old_egid = current->egid; | 533 | const struct cred *old; |
534 | struct cred *new; | ||
525 | int retval; | 535 | int retval; |
526 | 536 | ||
537 | new = prepare_creds(); | ||
538 | if (!new) | ||
539 | return -ENOMEM; | ||
540 | old = current_cred(); | ||
541 | |||
527 | retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID); | 542 | retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID); |
528 | if (retval) | 543 | if (retval) |
529 | return retval; | 544 | goto error; |
530 | 545 | ||
531 | if (capable(CAP_SETGID)) { | 546 | retval = -EPERM; |
532 | if (old_egid != gid) { | 547 | if (capable(CAP_SETGID)) |
533 | set_dumpable(current->mm, suid_dumpable); | 548 | new->gid = new->egid = new->sgid = new->fsgid = gid; |
534 | smp_wmb(); | 549 | else if (gid == old->gid || gid == old->sgid) |
535 | } | 550 | new->egid = new->fsgid = gid; |
536 | current->gid = current->egid = current->sgid = current->fsgid = gid; | ||
537 | } else if ((gid == current->gid) || (gid == current->sgid)) { | ||
538 | if (old_egid != gid) { | ||
539 | set_dumpable(current->mm, suid_dumpable); | ||
540 | smp_wmb(); | ||
541 | } | ||
542 | current->egid = current->fsgid = gid; | ||
543 | } | ||
544 | else | 551 | else |
545 | return -EPERM; | 552 | goto error; |
546 | 553 | ||
547 | key_fsgid_changed(current); | 554 | return commit_creds(new); |
548 | proc_id_connector(current, PROC_EVENT_GID); | 555 | |
549 | return 0; | 556 | error: |
557 | abort_creds(new); | ||
558 | return retval; | ||
550 | } | 559 | } |
551 | 560 | ||
552 | static int set_user(uid_t new_ruid, int dumpclear) | 561 | /* |
562 | * change the user struct in a credentials set to match the new UID | ||
563 | */ | ||
564 | static int set_user(struct cred *new) | ||
553 | { | 565 | { |
554 | struct user_struct *new_user; | 566 | struct user_struct *new_user; |
555 | 567 | ||
556 | new_user = alloc_uid(current->nsproxy->user_ns, new_ruid); | 568 | new_user = alloc_uid(current_user_ns(), new->uid); |
557 | if (!new_user) | 569 | if (!new_user) |
558 | return -EAGAIN; | 570 | return -EAGAIN; |
559 | 571 | ||
560 | if (atomic_read(&new_user->processes) >= | 572 | if (atomic_read(&new_user->processes) >= |
561 | current->signal->rlim[RLIMIT_NPROC].rlim_cur && | 573 | current->signal->rlim[RLIMIT_NPROC].rlim_cur && |
562 | new_user != current->nsproxy->user_ns->root_user) { | 574 | new_user != INIT_USER) { |
563 | free_uid(new_user); | 575 | free_uid(new_user); |
564 | return -EAGAIN; | 576 | return -EAGAIN; |
565 | } | 577 | } |
566 | 578 | ||
567 | switch_uid(new_user); | 579 | free_uid(new->user); |
568 | 580 | new->user = new_user; | |
569 | if (dumpclear) { | ||
570 | set_dumpable(current->mm, suid_dumpable); | ||
571 | smp_wmb(); | ||
572 | } | ||
573 | current->uid = new_ruid; | ||
574 | return 0; | 581 | return 0; |
575 | } | 582 | } |
576 | 583 | ||
@@ -591,54 +598,56 @@ static int set_user(uid_t new_ruid, int dumpclear) | |||
591 | */ | 598 | */ |
592 | asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) | 599 | asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) |
593 | { | 600 | { |
594 | int old_ruid, old_euid, old_suid, new_ruid, new_euid; | 601 | const struct cred *old; |
602 | struct cred *new; | ||
595 | int retval; | 603 | int retval; |
596 | 604 | ||
605 | new = prepare_creds(); | ||
606 | if (!new) | ||
607 | return -ENOMEM; | ||
608 | old = current_cred(); | ||
609 | |||
597 | retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE); | 610 | retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE); |
598 | if (retval) | 611 | if (retval) |
599 | return retval; | 612 | goto error; |
600 | |||
601 | new_ruid = old_ruid = current->uid; | ||
602 | new_euid = old_euid = current->euid; | ||
603 | old_suid = current->suid; | ||
604 | 613 | ||
614 | retval = -EPERM; | ||
605 | if (ruid != (uid_t) -1) { | 615 | if (ruid != (uid_t) -1) { |
606 | new_ruid = ruid; | 616 | new->uid = ruid; |
607 | if ((old_ruid != ruid) && | 617 | if (old->uid != ruid && |
608 | (current->euid != ruid) && | 618 | old->euid != ruid && |
609 | !capable(CAP_SETUID)) | 619 | !capable(CAP_SETUID)) |
610 | return -EPERM; | 620 | goto error; |
611 | } | 621 | } |
612 | 622 | ||
613 | if (euid != (uid_t) -1) { | 623 | if (euid != (uid_t) -1) { |
614 | new_euid = euid; | 624 | new->euid = euid; |
615 | if ((old_ruid != euid) && | 625 | if (old->uid != euid && |
616 | (current->euid != euid) && | 626 | old->euid != euid && |
617 | (current->suid != euid) && | 627 | old->suid != euid && |
618 | !capable(CAP_SETUID)) | 628 | !capable(CAP_SETUID)) |
619 | return -EPERM; | 629 | goto error; |
620 | } | 630 | } |
621 | 631 | ||
622 | if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0) | 632 | retval = -EAGAIN; |
623 | return -EAGAIN; | 633 | if (new->uid != old->uid && set_user(new) < 0) |
634 | goto error; | ||
624 | 635 | ||
625 | if (new_euid != old_euid) { | ||
626 | set_dumpable(current->mm, suid_dumpable); | ||
627 | smp_wmb(); | ||
628 | } | ||
629 | current->fsuid = current->euid = new_euid; | ||
630 | if (ruid != (uid_t) -1 || | 636 | if (ruid != (uid_t) -1 || |
631 | (euid != (uid_t) -1 && euid != old_ruid)) | 637 | (euid != (uid_t) -1 && euid != old->uid)) |
632 | current->suid = current->euid; | 638 | new->suid = new->euid; |
633 | current->fsuid = current->euid; | 639 | new->fsuid = new->euid; |
634 | 640 | ||
635 | key_fsuid_changed(current); | 641 | retval = security_task_fix_setuid(new, old, LSM_SETID_RE); |
636 | proc_id_connector(current, PROC_EVENT_UID); | 642 | if (retval < 0) |
637 | 643 | goto error; | |
638 | return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE); | ||
639 | } | ||
640 | 644 | ||
645 | return commit_creds(new); | ||
641 | 646 | ||
647 | error: | ||
648 | abort_creds(new); | ||
649 | return retval; | ||
650 | } | ||
642 | 651 | ||
643 | /* | 652 | /* |
644 | * setuid() is implemented like SysV with SAVED_IDS | 653 | * setuid() is implemented like SysV with SAVED_IDS |
@@ -653,36 +662,41 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) | |||
653 | */ | 662 | */ |
654 | asmlinkage long sys_setuid(uid_t uid) | 663 | asmlinkage long sys_setuid(uid_t uid) |
655 | { | 664 | { |
656 | int old_euid = current->euid; | 665 | const struct cred *old; |
657 | int old_ruid, old_suid, new_suid; | 666 | struct cred *new; |
658 | int retval; | 667 | int retval; |
659 | 668 | ||
669 | new = prepare_creds(); | ||
670 | if (!new) | ||
671 | return -ENOMEM; | ||
672 | old = current_cred(); | ||
673 | |||
660 | retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID); | 674 | retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID); |
661 | if (retval) | 675 | if (retval) |
662 | return retval; | 676 | goto error; |
663 | 677 | ||
664 | old_ruid = current->uid; | 678 | retval = -EPERM; |
665 | old_suid = current->suid; | ||
666 | new_suid = old_suid; | ||
667 | |||
668 | if (capable(CAP_SETUID)) { | 679 | if (capable(CAP_SETUID)) { |
669 | if (uid != old_ruid && set_user(uid, old_euid != uid) < 0) | 680 | new->suid = new->uid = uid; |
670 | return -EAGAIN; | 681 | if (uid != old->uid && set_user(new) < 0) { |
671 | new_suid = uid; | 682 | retval = -EAGAIN; |
672 | } else if ((uid != current->uid) && (uid != new_suid)) | 683 | goto error; |
673 | return -EPERM; | 684 | } |
674 | 685 | } else if (uid != old->uid && uid != new->suid) { | |
675 | if (old_euid != uid) { | 686 | goto error; |
676 | set_dumpable(current->mm, suid_dumpable); | ||
677 | smp_wmb(); | ||
678 | } | 687 | } |
679 | current->fsuid = current->euid = uid; | ||
680 | current->suid = new_suid; | ||
681 | 688 | ||
682 | key_fsuid_changed(current); | 689 | new->fsuid = new->euid = uid; |
683 | proc_id_connector(current, PROC_EVENT_UID); | 690 | |
691 | retval = security_task_fix_setuid(new, old, LSM_SETID_ID); | ||
692 | if (retval < 0) | ||
693 | goto error; | ||
684 | 694 | ||
685 | return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID); | 695 | return commit_creds(new); |
696 | |||
697 | error: | ||
698 | abort_creds(new); | ||
699 | return retval; | ||
686 | } | 700 | } |
687 | 701 | ||
688 | 702 | ||
@@ -692,54 +706,63 @@ asmlinkage long sys_setuid(uid_t uid) | |||
692 | */ | 706 | */ |
693 | asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) | 707 | asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) |
694 | { | 708 | { |
695 | int old_ruid = current->uid; | 709 | const struct cred *old; |
696 | int old_euid = current->euid; | 710 | struct cred *new; |
697 | int old_suid = current->suid; | ||
698 | int retval; | 711 | int retval; |
699 | 712 | ||
713 | new = prepare_creds(); | ||
714 | if (!new) | ||
715 | return -ENOMEM; | ||
716 | |||
700 | retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES); | 717 | retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES); |
701 | if (retval) | 718 | if (retval) |
702 | return retval; | 719 | goto error; |
720 | old = current_cred(); | ||
703 | 721 | ||
722 | retval = -EPERM; | ||
704 | if (!capable(CAP_SETUID)) { | 723 | if (!capable(CAP_SETUID)) { |
705 | if ((ruid != (uid_t) -1) && (ruid != current->uid) && | 724 | if (ruid != (uid_t) -1 && ruid != old->uid && |
706 | (ruid != current->euid) && (ruid != current->suid)) | 725 | ruid != old->euid && ruid != old->suid) |
707 | return -EPERM; | 726 | goto error; |
708 | if ((euid != (uid_t) -1) && (euid != current->uid) && | 727 | if (euid != (uid_t) -1 && euid != old->uid && |
709 | (euid != current->euid) && (euid != current->suid)) | 728 | euid != old->euid && euid != old->suid) |
710 | return -EPERM; | 729 | goto error; |
711 | if ((suid != (uid_t) -1) && (suid != current->uid) && | 730 | if (suid != (uid_t) -1 && suid != old->uid && |
712 | (suid != current->euid) && (suid != current->suid)) | 731 | suid != old->euid && suid != old->suid) |
713 | return -EPERM; | 732 | goto error; |
714 | } | 733 | } |
734 | |||
735 | retval = -EAGAIN; | ||
715 | if (ruid != (uid_t) -1) { | 736 | if (ruid != (uid_t) -1) { |
716 | if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0) | 737 | new->uid = ruid; |
717 | return -EAGAIN; | 738 | if (ruid != old->uid && set_user(new) < 0) |
739 | goto error; | ||
718 | } | 740 | } |
719 | if (euid != (uid_t) -1) { | 741 | if (euid != (uid_t) -1) |
720 | if (euid != current->euid) { | 742 | new->euid = euid; |
721 | set_dumpable(current->mm, suid_dumpable); | ||
722 | smp_wmb(); | ||
723 | } | ||
724 | current->euid = euid; | ||
725 | } | ||
726 | current->fsuid = current->euid; | ||
727 | if (suid != (uid_t) -1) | 743 | if (suid != (uid_t) -1) |
728 | current->suid = suid; | 744 | new->suid = suid; |
745 | new->fsuid = new->euid; | ||
746 | |||
747 | retval = security_task_fix_setuid(new, old, LSM_SETID_RES); | ||
748 | if (retval < 0) | ||
749 | goto error; | ||
729 | 750 | ||
730 | key_fsuid_changed(current); | 751 | return commit_creds(new); |
731 | proc_id_connector(current, PROC_EVENT_UID); | ||
732 | 752 | ||
733 | return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES); | 753 | error: |
754 | abort_creds(new); | ||
755 | return retval; | ||
734 | } | 756 | } |
735 | 757 | ||
736 | asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid) | 758 | asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid) |
737 | { | 759 | { |
760 | const struct cred *cred = current_cred(); | ||
738 | int retval; | 761 | int retval; |
739 | 762 | ||
740 | if (!(retval = put_user(current->uid, ruid)) && | 763 | if (!(retval = put_user(cred->uid, ruid)) && |
741 | !(retval = put_user(current->euid, euid))) | 764 | !(retval = put_user(cred->euid, euid))) |
742 | retval = put_user(current->suid, suid); | 765 | retval = put_user(cred->suid, suid); |
743 | 766 | ||
744 | return retval; | 767 | return retval; |
745 | } | 768 | } |
@@ -749,48 +772,55 @@ asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __us | |||
749 | */ | 772 | */ |
750 | asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) | 773 | asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) |
751 | { | 774 | { |
775 | const struct cred *old; | ||
776 | struct cred *new; | ||
752 | int retval; | 777 | int retval; |
753 | 778 | ||
779 | new = prepare_creds(); | ||
780 | if (!new) | ||
781 | return -ENOMEM; | ||
782 | old = current_cred(); | ||
783 | |||
754 | retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES); | 784 | retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES); |
755 | if (retval) | 785 | if (retval) |
756 | return retval; | 786 | goto error; |
757 | 787 | ||
788 | retval = -EPERM; | ||
758 | if (!capable(CAP_SETGID)) { | 789 | if (!capable(CAP_SETGID)) { |
759 | if ((rgid != (gid_t) -1) && (rgid != current->gid) && | 790 | if (rgid != (gid_t) -1 && rgid != old->gid && |
760 | (rgid != current->egid) && (rgid != current->sgid)) | 791 | rgid != old->egid && rgid != old->sgid) |
761 | return -EPERM; | 792 | goto error; |
762 | if ((egid != (gid_t) -1) && (egid != current->gid) && | 793 | if (egid != (gid_t) -1 && egid != old->gid && |
763 | (egid != current->egid) && (egid != current->sgid)) | 794 | egid != old->egid && egid != old->sgid) |
764 | return -EPERM; | 795 | goto error; |
765 | if ((sgid != (gid_t) -1) && (sgid != current->gid) && | 796 | if (sgid != (gid_t) -1 && sgid != old->gid && |
766 | (sgid != current->egid) && (sgid != current->sgid)) | 797 | sgid != old->egid && sgid != old->sgid) |
767 | return -EPERM; | 798 | goto error; |
768 | } | 799 | } |
769 | if (egid != (gid_t) -1) { | 800 | |
770 | if (egid != current->egid) { | ||
771 | set_dumpable(current->mm, suid_dumpable); | ||
772 | smp_wmb(); | ||
773 | } | ||
774 | current->egid = egid; | ||
775 | } | ||
776 | current->fsgid = current->egid; | ||
777 | if (rgid != (gid_t) -1) | 801 | if (rgid != (gid_t) -1) |
778 | current->gid = rgid; | 802 | new->gid = rgid; |
803 | if (egid != (gid_t) -1) | ||
804 | new->egid = egid; | ||
779 | if (sgid != (gid_t) -1) | 805 | if (sgid != (gid_t) -1) |
780 | current->sgid = sgid; | 806 | new->sgid = sgid; |
807 | new->fsgid = new->egid; | ||
781 | 808 | ||
782 | key_fsgid_changed(current); | 809 | return commit_creds(new); |
783 | proc_id_connector(current, PROC_EVENT_GID); | 810 | |
784 | return 0; | 811 | error: |
812 | abort_creds(new); | ||
813 | return retval; | ||
785 | } | 814 | } |
786 | 815 | ||
787 | asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid) | 816 | asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid) |
788 | { | 817 | { |
818 | const struct cred *cred = current_cred(); | ||
789 | int retval; | 819 | int retval; |
790 | 820 | ||
791 | if (!(retval = put_user(current->gid, rgid)) && | 821 | if (!(retval = put_user(cred->gid, rgid)) && |
792 | !(retval = put_user(current->egid, egid))) | 822 | !(retval = put_user(cred->egid, egid))) |
793 | retval = put_user(current->sgid, sgid); | 823 | retval = put_user(cred->sgid, sgid); |
794 | 824 | ||
795 | return retval; | 825 | return retval; |
796 | } | 826 | } |
@@ -804,27 +834,35 @@ asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __us | |||
804 | */ | 834 | */ |
805 | asmlinkage long sys_setfsuid(uid_t uid) | 835 | asmlinkage long sys_setfsuid(uid_t uid) |
806 | { | 836 | { |
807 | int old_fsuid; | 837 | const struct cred *old; |
838 | struct cred *new; | ||
839 | uid_t old_fsuid; | ||
808 | 840 | ||
809 | old_fsuid = current->fsuid; | 841 | new = prepare_creds(); |
810 | if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS)) | 842 | if (!new) |
811 | return old_fsuid; | 843 | return current_fsuid(); |
844 | old = current_cred(); | ||
845 | old_fsuid = old->fsuid; | ||
812 | 846 | ||
813 | if (uid == current->uid || uid == current->euid || | 847 | if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS) < 0) |
814 | uid == current->suid || uid == current->fsuid || | 848 | goto error; |
849 | |||
850 | if (uid == old->uid || uid == old->euid || | ||
851 | uid == old->suid || uid == old->fsuid || | ||
815 | capable(CAP_SETUID)) { | 852 | capable(CAP_SETUID)) { |
816 | if (uid != old_fsuid) { | 853 | if (uid != old_fsuid) { |
817 | set_dumpable(current->mm, suid_dumpable); | 854 | new->fsuid = uid; |
818 | smp_wmb(); | 855 | if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0) |
856 | goto change_okay; | ||
819 | } | 857 | } |
820 | current->fsuid = uid; | ||
821 | } | 858 | } |
822 | 859 | ||
823 | key_fsuid_changed(current); | 860 | error: |
824 | proc_id_connector(current, PROC_EVENT_UID); | 861 | abort_creds(new); |
825 | 862 | return old_fsuid; | |
826 | security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS); | ||
827 | 863 | ||
864 | change_okay: | ||
865 | commit_creds(new); | ||
828 | return old_fsuid; | 866 | return old_fsuid; |
829 | } | 867 | } |
830 | 868 | ||
@@ -833,23 +871,34 @@ asmlinkage long sys_setfsuid(uid_t uid) | |||
833 | */ | 871 | */ |
834 | asmlinkage long sys_setfsgid(gid_t gid) | 872 | asmlinkage long sys_setfsgid(gid_t gid) |
835 | { | 873 | { |
836 | int old_fsgid; | 874 | const struct cred *old; |
875 | struct cred *new; | ||
876 | gid_t old_fsgid; | ||
877 | |||
878 | new = prepare_creds(); | ||
879 | if (!new) | ||
880 | return current_fsgid(); | ||
881 | old = current_cred(); | ||
882 | old_fsgid = old->fsgid; | ||
837 | 883 | ||
838 | old_fsgid = current->fsgid; | ||
839 | if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS)) | 884 | if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS)) |
840 | return old_fsgid; | 885 | goto error; |
841 | 886 | ||
842 | if (gid == current->gid || gid == current->egid || | 887 | if (gid == old->gid || gid == old->egid || |
843 | gid == current->sgid || gid == current->fsgid || | 888 | gid == old->sgid || gid == old->fsgid || |
844 | capable(CAP_SETGID)) { | 889 | capable(CAP_SETGID)) { |
845 | if (gid != old_fsgid) { | 890 | if (gid != old_fsgid) { |
846 | set_dumpable(current->mm, suid_dumpable); | 891 | new->fsgid = gid; |
847 | smp_wmb(); | 892 | goto change_okay; |
848 | } | 893 | } |
849 | current->fsgid = gid; | ||
850 | key_fsgid_changed(current); | ||
851 | proc_id_connector(current, PROC_EVENT_GID); | ||
852 | } | 894 | } |
895 | |||
896 | error: | ||
897 | abort_creds(new); | ||
898 | return old_fsgid; | ||
899 | |||
900 | change_okay: | ||
901 | commit_creds(new); | ||
853 | return old_fsgid; | 902 | return old_fsgid; |
854 | } | 903 | } |
855 | 904 | ||
@@ -1118,7 +1167,7 @@ EXPORT_SYMBOL(groups_free); | |||
1118 | 1167 | ||
1119 | /* export the group_info to a user-space array */ | 1168 | /* export the group_info to a user-space array */ |
1120 | static int groups_to_user(gid_t __user *grouplist, | 1169 | static int groups_to_user(gid_t __user *grouplist, |
1121 | struct group_info *group_info) | 1170 | const struct group_info *group_info) |
1122 | { | 1171 | { |
1123 | int i; | 1172 | int i; |
1124 | unsigned int count = group_info->ngroups; | 1173 | unsigned int count = group_info->ngroups; |
@@ -1186,7 +1235,7 @@ static void groups_sort(struct group_info *group_info) | |||
1186 | } | 1235 | } |
1187 | 1236 | ||
1188 | /* a simple bsearch */ | 1237 | /* a simple bsearch */ |
1189 | int groups_search(struct group_info *group_info, gid_t grp) | 1238 | int groups_search(const struct group_info *group_info, gid_t grp) |
1190 | { | 1239 | { |
1191 | unsigned int left, right; | 1240 | unsigned int left, right; |
1192 | 1241 | ||
@@ -1208,51 +1257,74 @@ int groups_search(struct group_info *group_info, gid_t grp) | |||
1208 | return 0; | 1257 | return 0; |
1209 | } | 1258 | } |
1210 | 1259 | ||
1211 | /* validate and set current->group_info */ | 1260 | /** |
1212 | int set_current_groups(struct group_info *group_info) | 1261 | * set_groups - Change a group subscription in a set of credentials |
1262 | * @new: The newly prepared set of credentials to alter | ||
1263 | * @group_info: The group list to install | ||
1264 | * | ||
1265 | * Validate a group subscription and, if valid, insert it into a set | ||
1266 | * of credentials. | ||
1267 | */ | ||
1268 | int set_groups(struct cred *new, struct group_info *group_info) | ||
1213 | { | 1269 | { |
1214 | int retval; | 1270 | int retval; |
1215 | struct group_info *old_info; | ||
1216 | 1271 | ||
1217 | retval = security_task_setgroups(group_info); | 1272 | retval = security_task_setgroups(group_info); |
1218 | if (retval) | 1273 | if (retval) |
1219 | return retval; | 1274 | return retval; |
1220 | 1275 | ||
1276 | put_group_info(new->group_info); | ||
1221 | groups_sort(group_info); | 1277 | groups_sort(group_info); |
1222 | get_group_info(group_info); | 1278 | get_group_info(group_info); |
1279 | new->group_info = group_info; | ||
1280 | return 0; | ||
1281 | } | ||
1282 | |||
1283 | EXPORT_SYMBOL(set_groups); | ||
1223 | 1284 | ||
1224 | task_lock(current); | 1285 | /** |
1225 | old_info = current->group_info; | 1286 | * set_current_groups - Change current's group subscription |
1226 | current->group_info = group_info; | 1287 | * @group_info: The group list to impose |
1227 | task_unlock(current); | 1288 | * |
1289 | * Validate a group subscription and, if valid, impose it upon current's task | ||
1290 | * security record. | ||
1291 | */ | ||
1292 | int set_current_groups(struct group_info *group_info) | ||
1293 | { | ||
1294 | struct cred *new; | ||
1295 | int ret; | ||
1228 | 1296 | ||
1229 | put_group_info(old_info); | 1297 | new = prepare_creds(); |
1298 | if (!new) | ||
1299 | return -ENOMEM; | ||
1230 | 1300 | ||
1231 | return 0; | 1301 | ret = set_groups(new, group_info); |
1302 | if (ret < 0) { | ||
1303 | abort_creds(new); | ||
1304 | return ret; | ||
1305 | } | ||
1306 | |||
1307 | return commit_creds(new); | ||
1232 | } | 1308 | } |
1233 | 1309 | ||
1234 | EXPORT_SYMBOL(set_current_groups); | 1310 | EXPORT_SYMBOL(set_current_groups); |
1235 | 1311 | ||
1236 | asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist) | 1312 | asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist) |
1237 | { | 1313 | { |
1238 | int i = 0; | 1314 | const struct cred *cred = current_cred(); |
1239 | 1315 | int i; | |
1240 | /* | ||
1241 | * SMP: Nobody else can change our grouplist. Thus we are | ||
1242 | * safe. | ||
1243 | */ | ||
1244 | 1316 | ||
1245 | if (gidsetsize < 0) | 1317 | if (gidsetsize < 0) |
1246 | return -EINVAL; | 1318 | return -EINVAL; |
1247 | 1319 | ||
1248 | /* no need to grab task_lock here; it cannot change */ | 1320 | /* no need to grab task_lock here; it cannot change */ |
1249 | i = current->group_info->ngroups; | 1321 | i = cred->group_info->ngroups; |
1250 | if (gidsetsize) { | 1322 | if (gidsetsize) { |
1251 | if (i > gidsetsize) { | 1323 | if (i > gidsetsize) { |
1252 | i = -EINVAL; | 1324 | i = -EINVAL; |
1253 | goto out; | 1325 | goto out; |
1254 | } | 1326 | } |
1255 | if (groups_to_user(grouplist, current->group_info)) { | 1327 | if (groups_to_user(grouplist, cred->group_info)) { |
1256 | i = -EFAULT; | 1328 | i = -EFAULT; |
1257 | goto out; | 1329 | goto out; |
1258 | } | 1330 | } |
@@ -1296,9 +1368,11 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist) | |||
1296 | */ | 1368 | */ |
1297 | int in_group_p(gid_t grp) | 1369 | int in_group_p(gid_t grp) |
1298 | { | 1370 | { |
1371 | const struct cred *cred = current_cred(); | ||
1299 | int retval = 1; | 1372 | int retval = 1; |
1300 | if (grp != current->fsgid) | 1373 | |
1301 | retval = groups_search(current->group_info, grp); | 1374 | if (grp != cred->fsgid) |
1375 | retval = groups_search(cred->group_info, grp); | ||
1302 | return retval; | 1376 | return retval; |
1303 | } | 1377 | } |
1304 | 1378 | ||
@@ -1306,9 +1380,11 @@ EXPORT_SYMBOL(in_group_p); | |||
1306 | 1380 | ||
1307 | int in_egroup_p(gid_t grp) | 1381 | int in_egroup_p(gid_t grp) |
1308 | { | 1382 | { |
1383 | const struct cred *cred = current_cred(); | ||
1309 | int retval = 1; | 1384 | int retval = 1; |
1310 | if (grp != current->egid) | 1385 | |
1311 | retval = groups_search(current->group_info, grp); | 1386 | if (grp != cred->egid) |
1387 | retval = groups_search(cred->group_info, grp); | ||
1312 | return retval; | 1388 | return retval; |
1313 | } | 1389 | } |
1314 | 1390 | ||
@@ -1624,50 +1700,56 @@ asmlinkage long sys_umask(int mask) | |||
1624 | asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, | 1700 | asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, |
1625 | unsigned long arg4, unsigned long arg5) | 1701 | unsigned long arg4, unsigned long arg5) |
1626 | { | 1702 | { |
1627 | long error = 0; | 1703 | struct task_struct *me = current; |
1704 | unsigned char comm[sizeof(me->comm)]; | ||
1705 | long error; | ||
1628 | 1706 | ||
1629 | if (security_task_prctl(option, arg2, arg3, arg4, arg5, &error)) | 1707 | error = security_task_prctl(option, arg2, arg3, arg4, arg5); |
1708 | if (error != -ENOSYS) | ||
1630 | return error; | 1709 | return error; |
1631 | 1710 | ||
1711 | error = 0; | ||
1632 | switch (option) { | 1712 | switch (option) { |
1633 | case PR_SET_PDEATHSIG: | 1713 | case PR_SET_PDEATHSIG: |
1634 | if (!valid_signal(arg2)) { | 1714 | if (!valid_signal(arg2)) { |
1635 | error = -EINVAL; | 1715 | error = -EINVAL; |
1636 | break; | 1716 | break; |
1637 | } | 1717 | } |
1638 | current->pdeath_signal = arg2; | 1718 | me->pdeath_signal = arg2; |
1719 | error = 0; | ||
1639 | break; | 1720 | break; |
1640 | case PR_GET_PDEATHSIG: | 1721 | case PR_GET_PDEATHSIG: |
1641 | error = put_user(current->pdeath_signal, (int __user *)arg2); | 1722 | error = put_user(me->pdeath_signal, (int __user *)arg2); |
1642 | break; | 1723 | break; |
1643 | case PR_GET_DUMPABLE: | 1724 | case PR_GET_DUMPABLE: |
1644 | error = get_dumpable(current->mm); | 1725 | error = get_dumpable(me->mm); |
1645 | break; | 1726 | break; |
1646 | case PR_SET_DUMPABLE: | 1727 | case PR_SET_DUMPABLE: |
1647 | if (arg2 < 0 || arg2 > 1) { | 1728 | if (arg2 < 0 || arg2 > 1) { |
1648 | error = -EINVAL; | 1729 | error = -EINVAL; |
1649 | break; | 1730 | break; |
1650 | } | 1731 | } |
1651 | set_dumpable(current->mm, arg2); | 1732 | set_dumpable(me->mm, arg2); |
1733 | error = 0; | ||
1652 | break; | 1734 | break; |
1653 | 1735 | ||
1654 | case PR_SET_UNALIGN: | 1736 | case PR_SET_UNALIGN: |
1655 | error = SET_UNALIGN_CTL(current, arg2); | 1737 | error = SET_UNALIGN_CTL(me, arg2); |
1656 | break; | 1738 | break; |
1657 | case PR_GET_UNALIGN: | 1739 | case PR_GET_UNALIGN: |
1658 | error = GET_UNALIGN_CTL(current, arg2); | 1740 | error = GET_UNALIGN_CTL(me, arg2); |
1659 | break; | 1741 | break; |
1660 | case PR_SET_FPEMU: | 1742 | case PR_SET_FPEMU: |
1661 | error = SET_FPEMU_CTL(current, arg2); | 1743 | error = SET_FPEMU_CTL(me, arg2); |
1662 | break; | 1744 | break; |
1663 | case PR_GET_FPEMU: | 1745 | case PR_GET_FPEMU: |
1664 | error = GET_FPEMU_CTL(current, arg2); | 1746 | error = GET_FPEMU_CTL(me, arg2); |
1665 | break; | 1747 | break; |
1666 | case PR_SET_FPEXC: | 1748 | case PR_SET_FPEXC: |
1667 | error = SET_FPEXC_CTL(current, arg2); | 1749 | error = SET_FPEXC_CTL(me, arg2); |
1668 | break; | 1750 | break; |
1669 | case PR_GET_FPEXC: | 1751 | case PR_GET_FPEXC: |
1670 | error = GET_FPEXC_CTL(current, arg2); | 1752 | error = GET_FPEXC_CTL(me, arg2); |
1671 | break; | 1753 | break; |
1672 | case PR_GET_TIMING: | 1754 | case PR_GET_TIMING: |
1673 | error = PR_TIMING_STATISTICAL; | 1755 | error = PR_TIMING_STATISTICAL; |
@@ -1675,33 +1757,28 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
1675 | case PR_SET_TIMING: | 1757 | case PR_SET_TIMING: |
1676 | if (arg2 != PR_TIMING_STATISTICAL) | 1758 | if (arg2 != PR_TIMING_STATISTICAL) |
1677 | error = -EINVAL; | 1759 | error = -EINVAL; |
1760 | else | ||
1761 | error = 0; | ||
1678 | break; | 1762 | break; |
1679 | 1763 | ||
1680 | case PR_SET_NAME: { | 1764 | case PR_SET_NAME: |
1681 | struct task_struct *me = current; | 1765 | comm[sizeof(me->comm)-1] = 0; |
1682 | unsigned char ncomm[sizeof(me->comm)]; | 1766 | if (strncpy_from_user(comm, (char __user *)arg2, |
1683 | 1767 | sizeof(me->comm) - 1) < 0) | |
1684 | ncomm[sizeof(me->comm)-1] = 0; | ||
1685 | if (strncpy_from_user(ncomm, (char __user *)arg2, | ||
1686 | sizeof(me->comm)-1) < 0) | ||
1687 | return -EFAULT; | 1768 | return -EFAULT; |
1688 | set_task_comm(me, ncomm); | 1769 | set_task_comm(me, comm); |
1689 | return 0; | 1770 | return 0; |
1690 | } | 1771 | case PR_GET_NAME: |
1691 | case PR_GET_NAME: { | 1772 | get_task_comm(comm, me); |
1692 | struct task_struct *me = current; | 1773 | if (copy_to_user((char __user *)arg2, comm, |
1693 | unsigned char tcomm[sizeof(me->comm)]; | 1774 | sizeof(comm))) |
1694 | |||
1695 | get_task_comm(tcomm, me); | ||
1696 | if (copy_to_user((char __user *)arg2, tcomm, sizeof(tcomm))) | ||
1697 | return -EFAULT; | 1775 | return -EFAULT; |
1698 | return 0; | 1776 | return 0; |
1699 | } | ||
1700 | case PR_GET_ENDIAN: | 1777 | case PR_GET_ENDIAN: |
1701 | error = GET_ENDIAN(current, arg2); | 1778 | error = GET_ENDIAN(me, arg2); |
1702 | break; | 1779 | break; |
1703 | case PR_SET_ENDIAN: | 1780 | case PR_SET_ENDIAN: |
1704 | error = SET_ENDIAN(current, arg2); | 1781 | error = SET_ENDIAN(me, arg2); |
1705 | break; | 1782 | break; |
1706 | 1783 | ||
1707 | case PR_GET_SECCOMP: | 1784 | case PR_GET_SECCOMP: |
@@ -1725,6 +1802,7 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
1725 | current->default_timer_slack_ns; | 1802 | current->default_timer_slack_ns; |
1726 | else | 1803 | else |
1727 | current->timer_slack_ns = arg2; | 1804 | current->timer_slack_ns = arg2; |
1805 | error = 0; | ||
1728 | break; | 1806 | break; |
1729 | default: | 1807 | default: |
1730 | error = -EINVAL; | 1808 | error = -EINVAL; |