aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/sys.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-11-13 18:39:23 -0500
committerJames Morris <jmorris@namei.org>2008-11-13 18:39:23 -0500
commitd84f4f992cbd76e8f39c488cf0c5d123843923b1 (patch)
treefc4a0349c42995715b93d0f7a3c78e9ea9b3f36e /kernel/sys.c
parent745ca2475a6ac596e3d8d37c2759c0fbe2586227 (diff)
CRED: Inaugurate COW credentials
Inaugurate copy-on-write credentials management. This uses RCU to manage the credentials pointer in the task_struct with respect to accesses by other tasks. A process may only modify its own credentials, and so does not need locking to access or modify its own credentials. A mutex (cred_replace_mutex) is added to the task_struct to control the effect of PTRACE_ATTACHED on credential calculations, particularly with respect to execve(). With this patch, the contents of an active credentials struct may not be changed directly; rather a new set of credentials must be prepared, modified and committed using something like the following sequence of events: struct cred *new = prepare_creds(); int ret = blah(new); if (ret < 0) { abort_creds(new); return ret; } return commit_creds(new); There are some exceptions to this rule: the keyrings pointed to by the active credentials may be instantiated - keyrings violate the COW rule as managing COW keyrings is tricky, given that it is possible for a task to directly alter the keys in a keyring in use by another task. To help enforce this, various pointers to sets of credentials, such as those in the task_struct, are declared const. The purpose of this is compile-time discouragement of altering credentials through those pointers. Once a set of credentials has been made public through one of these pointers, it may not be modified, except under special circumstances: (1) Its reference count may incremented and decremented. (2) The keyrings to which it points may be modified, but not replaced. The only safe way to modify anything else is to create a replacement and commit using the functions described in Documentation/credentials.txt (which will be added by a later patch). This patch and the preceding patches have been tested with the LTP SELinux testsuite. This patch makes several logical sets of alteration: (1) execve(). This now prepares and commits credentials in various places in the security code rather than altering the current creds directly. (2) Temporary credential overrides. do_coredump() and sys_faccessat() now prepare their own credentials and temporarily override the ones currently on the acting thread, whilst preventing interference from other threads by holding cred_replace_mutex on the thread being dumped. This will be replaced in a future patch by something that hands down the credentials directly to the functions being called, rather than altering the task's objective credentials. (3) LSM interface. A number of functions have been changed, added or removed: (*) security_capset_check(), ->capset_check() (*) security_capset_set(), ->capset_set() Removed in favour of security_capset(). (*) security_capset(), ->capset() New. This is passed a pointer to the new creds, a pointer to the old creds and the proposed capability sets. It should fill in the new creds or return an error. All pointers, barring the pointer to the new creds, are now const. (*) security_bprm_apply_creds(), ->bprm_apply_creds() Changed; now returns a value, which will cause the process to be killed if it's an error. (*) security_task_alloc(), ->task_alloc_security() Removed in favour of security_prepare_creds(). (*) security_cred_free(), ->cred_free() New. Free security data attached to cred->security. (*) security_prepare_creds(), ->cred_prepare() New. Duplicate any security data attached to cred->security. (*) security_commit_creds(), ->cred_commit() New. Apply any security effects for the upcoming installation of new security by commit_creds(). (*) security_task_post_setuid(), ->task_post_setuid() Removed in favour of security_task_fix_setuid(). (*) security_task_fix_setuid(), ->task_fix_setuid() Fix up the proposed new credentials for setuid(). This is used by cap_set_fix_setuid() to implicitly adjust capabilities in line with setuid() changes. Changes are made to the new credentials, rather than the task itself as in security_task_post_setuid(). (*) security_task_reparent_to_init(), ->task_reparent_to_init() Removed. Instead the task being reparented to init is referred directly to init's credentials. NOTE! This results in the loss of some state: SELinux's osid no longer records the sid of the thread that forked it. (*) security_key_alloc(), ->key_alloc() (*) security_key_permission(), ->key_permission() Changed. These now take cred pointers rather than task pointers to refer to the security context. (4) sys_capset(). This has been simplified and uses less locking. The LSM functions it calls have been merged. (5) reparent_to_kthreadd(). This gives the current thread the same credentials as init by simply using commit_thread() to point that way. (6) __sigqueue_alloc() and switch_uid() __sigqueue_alloc() can't stop the target task from changing its creds beneath it, so this function gets a reference to the currently applicable user_struct which it then passes into the sigqueue struct it returns if successful. switch_uid() is now called from commit_creds(), and possibly should be folded into that. commit_creds() should take care of protecting __sigqueue_alloc(). (7) [sg]et[ug]id() and co and [sg]et_current_groups. The set functions now all use prepare_creds(), commit_creds() and abort_creds() to build and check a new set of credentials before applying it. security_task_set[ug]id() is called inside the prepared section. This guarantees that nothing else will affect the creds until we've finished. The calling of set_dumpable() has been moved into commit_creds(). Much of the functionality of set_user() has been moved into commit_creds(). The get functions all simply access the data directly. (8) security_task_prctl() and cap_task_prctl(). security_task_prctl() has been modified to return -ENOSYS if it doesn't want to handle a function, or otherwise return the return value directly rather than through an argument. Additionally, cap_task_prctl() now prepares a new set of credentials, even if it doesn't end up using it. (9) Keyrings. A number of changes have been made to the keyrings code: (a) switch_uid_keyring(), copy_keys(), exit_keys() and suid_keys() have all been dropped and built in to the credentials functions directly. They may want separating out again later. (b) key_alloc() and search_process_keyrings() now take a cred pointer rather than a task pointer to specify the security context. (c) copy_creds() gives a new thread within the same thread group a new thread keyring if its parent had one, otherwise it discards the thread keyring. (d) The authorisation key now points directly to the credentials to extend the search into rather pointing to the task that carries them. (e) Installing thread, process or session keyrings causes a new set of credentials to be created, even though it's not strictly necessary for process or session keyrings (they're shared). (10) Usermode helper. The usermode helper code now carries a cred struct pointer in its subprocess_info struct instead of a new session keyring pointer. This set of credentials is derived from init_cred and installed on the new process after it has been cloned. call_usermodehelper_setup() allocates the new credentials and call_usermodehelper_freeinfo() discards them if they haven't been used. A special cred function (prepare_usermodeinfo_creds()) is provided specifically for call_usermodehelper_setup() to call. call_usermodehelper_setkeys() adjusts the credentials to sport the supplied keyring as the new session keyring. (11) SELinux. SELinux has a number of changes, in addition to those to support the LSM interface changes mentioned above: (a) selinux_setprocattr() no longer does its check for whether the current ptracer can access processes with the new SID inside the lock that covers getting the ptracer's SID. Whilst this lock ensures that the check is done with the ptracer pinned, the result is only valid until the lock is released, so there's no point doing it inside the lock. (12) is_single_threaded(). This function has been extracted from selinux_setprocattr() and put into a file of its own in the lib/ directory as join_session_keyring() now wants to use it too. The code in SELinux just checked to see whether a task shared mm_structs with other tasks (CLONE_VM), but that isn't good enough. We really want to know if they're part of the same thread group (CLONE_THREAD). (13) nfsd. The NFS server daemon now has to use the COW credentials to set the credentials it is going to use. It really needs to pass the credentials down to the functions it calls, but it can't do that until other patches in this series have been applied. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: James Morris <jmorris@namei.org> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'kernel/sys.c')
-rw-r--r--kernel/sys.c450
1 files changed, 245 insertions, 205 deletions
diff --git a/kernel/sys.c b/kernel/sys.c
index ccc9eb736d35..ab735040468a 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -180,7 +180,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
180 } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); 180 } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
181 break; 181 break;
182 case PRIO_USER: 182 case PRIO_USER:
183 user = cred->user; 183 user = (struct user_struct *) cred->user;
184 if (!who) 184 if (!who)
185 who = cred->uid; 185 who = cred->uid;
186 else if ((who != cred->uid) && 186 else if ((who != cred->uid) &&
@@ -479,47 +479,48 @@ void ctrl_alt_del(void)
479 */ 479 */
480asmlinkage long sys_setregid(gid_t rgid, gid_t egid) 480asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
481{ 481{
482 struct cred *cred = current->cred; 482 const struct cred *old;
483 int old_rgid = cred->gid; 483 struct cred *new;
484 int old_egid = cred->egid;
485 int new_rgid = old_rgid;
486 int new_egid = old_egid;
487 int retval; 484 int retval;
488 485
486 new = prepare_creds();
487 if (!new)
488 return -ENOMEM;
489 old = current_cred();
490
489 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);
490 if (retval) 492 if (retval)
491 return retval; 493 goto error;
492 494
495 retval = -EPERM;
493 if (rgid != (gid_t) -1) { 496 if (rgid != (gid_t) -1) {
494 if ((old_rgid == rgid) || 497 if (old->gid == rgid ||
495 (cred->egid == rgid) || 498 old->egid == rgid ||
496 capable(CAP_SETGID)) 499 capable(CAP_SETGID))
497 new_rgid = rgid; 500 new->gid = rgid;
498 else 501 else
499 return -EPERM; 502 goto error;
500 } 503 }
501 if (egid != (gid_t) -1) { 504 if (egid != (gid_t) -1) {
502 if ((old_rgid == egid) || 505 if (old->gid == egid ||
503 (cred->egid == egid) || 506 old->egid == egid ||
504 (cred->sgid == egid) || 507 old->sgid == egid ||
505 capable(CAP_SETGID)) 508 capable(CAP_SETGID))
506 new_egid = egid; 509 new->egid = egid;
507 else 510 else
508 return -EPERM; 511 goto error;
509 }
510 if (new_egid != old_egid) {
511 set_dumpable(current->mm, suid_dumpable);
512 smp_wmb();
513 } 512 }
513
514 if (rgid != (gid_t) -1 || 514 if (rgid != (gid_t) -1 ||
515 (egid != (gid_t) -1 && egid != old_rgid)) 515 (egid != (gid_t) -1 && egid != old->gid))
516 cred->sgid = new_egid; 516 new->sgid = new->egid;
517 cred->fsgid = new_egid; 517 new->fsgid = new->egid;
518 cred->egid = new_egid; 518
519 cred->gid = new_rgid; 519 return commit_creds(new);
520 key_fsgid_changed(current); 520
521 proc_id_connector(current, PROC_EVENT_GID); 521error:
522 return 0; 522 abort_creds(new);
523 return retval;
523} 524}
524 525
525/* 526/*
@@ -529,40 +530,42 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
529 */ 530 */
530asmlinkage long sys_setgid(gid_t gid) 531asmlinkage long sys_setgid(gid_t gid)
531{ 532{
532 struct cred *cred = current->cred; 533 const struct cred *old;
533 int old_egid = cred->egid; 534 struct cred *new;
534 int retval; 535 int retval;
535 536
537 new = prepare_creds();
538 if (!new)
539 return -ENOMEM;
540 old = current_cred();
541
536 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);
537 if (retval) 543 if (retval)
538 return retval; 544 goto error;
539 545
540 if (capable(CAP_SETGID)) { 546 retval = -EPERM;
541 if (old_egid != gid) { 547 if (capable(CAP_SETGID))
542 set_dumpable(current->mm, suid_dumpable); 548 new->gid = new->egid = new->sgid = new->fsgid = gid;
543 smp_wmb(); 549 else if (gid == old->gid || gid == old->sgid)
544 } 550 new->egid = new->fsgid = gid;
545 cred->gid = cred->egid = cred->sgid = cred->fsgid = gid;
546 } else if ((gid == cred->gid) || (gid == cred->sgid)) {
547 if (old_egid != gid) {
548 set_dumpable(current->mm, suid_dumpable);
549 smp_wmb();
550 }
551 cred->egid = cred->fsgid = gid;
552 }
553 else 551 else
554 return -EPERM; 552 goto error;
555 553
556 key_fsgid_changed(current); 554 return commit_creds(new);
557 proc_id_connector(current, PROC_EVENT_GID); 555
558 return 0; 556error:
557 abort_creds(new);
558 return retval;
559} 559}
560 560
561static 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 */
564static int set_user(struct cred *new)
562{ 565{
563 struct user_struct *new_user; 566 struct user_struct *new_user;
564 567
565 new_user = alloc_uid(current->nsproxy->user_ns, new_ruid); 568 new_user = alloc_uid(current->nsproxy->user_ns, new->uid);
566 if (!new_user) 569 if (!new_user)
567 return -EAGAIN; 570 return -EAGAIN;
568 571
@@ -573,13 +576,8 @@ static int set_user(uid_t new_ruid, int dumpclear)
573 return -EAGAIN; 576 return -EAGAIN;
574 } 577 }
575 578
576 switch_uid(new_user); 579 free_uid(new->user);
577 580 new->user = new_user;
578 if (dumpclear) {
579 set_dumpable(current->mm, suid_dumpable);
580 smp_wmb();
581 }
582 current->cred->uid = new_ruid;
583 return 0; 581 return 0;
584} 582}
585 583
@@ -600,55 +598,56 @@ static int set_user(uid_t new_ruid, int dumpclear)
600 */ 598 */
601asmlinkage long sys_setreuid(uid_t ruid, uid_t euid) 599asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
602{ 600{
603 struct cred *cred = current->cred; 601 const struct cred *old;
604 int old_ruid, old_euid, old_suid, new_ruid, new_euid; 602 struct cred *new;
605 int retval; 603 int retval;
606 604
605 new = prepare_creds();
606 if (!new)
607 return -ENOMEM;
608 old = current_cred();
609
607 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);
608 if (retval) 611 if (retval)
609 return retval; 612 goto error;
610
611 new_ruid = old_ruid = cred->uid;
612 new_euid = old_euid = cred->euid;
613 old_suid = cred->suid;
614 613
614 retval = -EPERM;
615 if (ruid != (uid_t) -1) { 615 if (ruid != (uid_t) -1) {
616 new_ruid = ruid; 616 new->uid = ruid;
617 if ((old_ruid != ruid) && 617 if (old->uid != ruid &&
618 (cred->euid != ruid) && 618 old->euid != ruid &&
619 !capable(CAP_SETUID)) 619 !capable(CAP_SETUID))
620 return -EPERM; 620 goto error;
621 } 621 }
622 622
623 if (euid != (uid_t) -1) { 623 if (euid != (uid_t) -1) {
624 new_euid = euid; 624 new->euid = euid;
625 if ((old_ruid != euid) && 625 if (old->uid != euid &&
626 (cred->euid != euid) && 626 old->euid != euid &&
627 (cred->suid != euid) && 627 old->suid != euid &&
628 !capable(CAP_SETUID)) 628 !capable(CAP_SETUID))
629 return -EPERM; 629 goto error;
630 } 630 }
631 631
632 if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0) 632 retval = -EAGAIN;
633 return -EAGAIN; 633 if (new->uid != old->uid && set_user(new) < 0)
634 goto error;
634 635
635 if (new_euid != old_euid) {
636 set_dumpable(current->mm, suid_dumpable);
637 smp_wmb();
638 }
639 cred->fsuid = cred->euid = new_euid;
640 if (ruid != (uid_t) -1 || 636 if (ruid != (uid_t) -1 ||
641 (euid != (uid_t) -1 && euid != old_ruid)) 637 (euid != (uid_t) -1 && euid != old->uid))
642 cred->suid = cred->euid; 638 new->suid = new->euid;
643 cred->fsuid = cred->euid; 639 new->fsuid = new->euid;
644
645 key_fsuid_changed(current);
646 proc_id_connector(current, PROC_EVENT_UID);
647 640
648 return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE); 641 retval = security_task_fix_setuid(new, old, LSM_SETID_RE);
649} 642 if (retval < 0)
643 goto error;
650 644
645 return commit_creds(new);
651 646
647error:
648 abort_creds(new);
649 return retval;
650}
652 651
653/* 652/*
654 * setuid() is implemented like SysV with SAVED_IDS 653 * setuid() is implemented like SysV with SAVED_IDS
@@ -663,37 +662,41 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
663 */ 662 */
664asmlinkage long sys_setuid(uid_t uid) 663asmlinkage long sys_setuid(uid_t uid)
665{ 664{
666 struct cred *cred = current->cred; 665 const struct cred *old;
667 int old_euid = cred->euid; 666 struct cred *new;
668 int old_ruid, old_suid, new_suid;
669 int retval; 667 int retval;
670 668
669 new = prepare_creds();
670 if (!new)
671 return -ENOMEM;
672 old = current_cred();
673
671 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);
672 if (retval) 675 if (retval)
673 return retval; 676 goto error;
674 677
675 old_ruid = cred->uid; 678 retval = -EPERM;
676 old_suid = cred->suid;
677 new_suid = old_suid;
678
679 if (capable(CAP_SETUID)) { 679 if (capable(CAP_SETUID)) {
680 if (uid != old_ruid && set_user(uid, old_euid != uid) < 0) 680 new->suid = new->uid = uid;
681 return -EAGAIN; 681 if (uid != old->uid && set_user(new) < 0) {
682 new_suid = uid; 682 retval = -EAGAIN;
683 } else if ((uid != cred->uid) && (uid != new_suid)) 683 goto error;
684 return -EPERM; 684 }
685 685 } else if (uid != old->uid && uid != new->suid) {
686 if (old_euid != uid) { 686 goto error;
687 set_dumpable(current->mm, suid_dumpable);
688 smp_wmb();
689 } 687 }
690 cred->fsuid = cred->euid = uid;
691 cred->suid = new_suid;
692 688
693 key_fsuid_changed(current); 689 new->fsuid = new->euid = uid;
694 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;
694
695 return commit_creds(new);
695 696
696 return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID); 697error:
698 abort_creds(new);
699 return retval;
697} 700}
698 701
699 702
@@ -703,47 +706,53 @@ asmlinkage long sys_setuid(uid_t uid)
703 */ 706 */
704asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) 707asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
705{ 708{
706 struct cred *cred = current->cred; 709 const struct cred *old;
707 int old_ruid = cred->uid; 710 struct cred *new;
708 int old_euid = cred->euid;
709 int old_suid = cred->suid;
710 int retval; 711 int retval;
711 712
713 new = prepare_creds();
714 if (!new)
715 return -ENOMEM;
716
712 retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES); 717 retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
713 if (retval) 718 if (retval)
714 return retval; 719 goto error;
720 old = current_cred();
715 721
722 retval = -EPERM;
716 if (!capable(CAP_SETUID)) { 723 if (!capable(CAP_SETUID)) {
717 if ((ruid != (uid_t) -1) && (ruid != cred->uid) && 724 if (ruid != (uid_t) -1 && ruid != old->uid &&
718 (ruid != cred->euid) && (ruid != cred->suid)) 725 ruid != old->euid && ruid != old->suid)
719 return -EPERM; 726 goto error;
720 if ((euid != (uid_t) -1) && (euid != cred->uid) && 727 if (euid != (uid_t) -1 && euid != old->uid &&
721 (euid != cred->euid) && (euid != cred->suid)) 728 euid != old->euid && euid != old->suid)
722 return -EPERM; 729 goto error;
723 if ((suid != (uid_t) -1) && (suid != cred->uid) && 730 if (suid != (uid_t) -1 && suid != old->uid &&
724 (suid != cred->euid) && (suid != cred->suid)) 731 suid != old->euid && suid != old->suid)
725 return -EPERM; 732 goto error;
726 } 733 }
734
735 retval = -EAGAIN;
727 if (ruid != (uid_t) -1) { 736 if (ruid != (uid_t) -1) {
728 if (ruid != cred->uid && 737 new->uid = ruid;
729 set_user(ruid, euid != cred->euid) < 0) 738 if (ruid != old->uid && set_user(new) < 0)
730 return -EAGAIN; 739 goto error;
731 } 740 }
732 if (euid != (uid_t) -1) { 741 if (euid != (uid_t) -1)
733 if (euid != cred->euid) { 742 new->euid = euid;
734 set_dumpable(current->mm, suid_dumpable);
735 smp_wmb();
736 }
737 cred->euid = euid;
738 }
739 cred->fsuid = cred->euid;
740 if (suid != (uid_t) -1) 743 if (suid != (uid_t) -1)
741 cred->suid = suid; 744 new->suid = suid;
745 new->fsuid = new->euid;
742 746
743 key_fsuid_changed(current); 747 retval = security_task_fix_setuid(new, old, LSM_SETID_RES);
744 proc_id_connector(current, PROC_EVENT_UID); 748 if (retval < 0)
749 goto error;
745 750
746 return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES); 751 return commit_creds(new);
752
753error:
754 abort_creds(new);
755 return retval;
747} 756}
748 757
749asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid) 758asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid)
@@ -763,40 +772,45 @@ asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __us
763 */ 772 */
764asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) 773asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
765{ 774{
766 struct cred *cred = current->cred; 775 const struct cred *old;
776 struct cred *new;
767 int retval; 777 int retval;
768 778
779 new = prepare_creds();
780 if (!new)
781 return -ENOMEM;
782 old = current_cred();
783
769 retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES); 784 retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
770 if (retval) 785 if (retval)
771 return retval; 786 goto error;
772 787
788 retval = -EPERM;
773 if (!capable(CAP_SETGID)) { 789 if (!capable(CAP_SETGID)) {
774 if ((rgid != (gid_t) -1) && (rgid != cred->gid) && 790 if (rgid != (gid_t) -1 && rgid != old->gid &&
775 (rgid != cred->egid) && (rgid != cred->sgid)) 791 rgid != old->egid && rgid != old->sgid)
776 return -EPERM; 792 goto error;
777 if ((egid != (gid_t) -1) && (egid != cred->gid) && 793 if (egid != (gid_t) -1 && egid != old->gid &&
778 (egid != cred->egid) && (egid != cred->sgid)) 794 egid != old->egid && egid != old->sgid)
779 return -EPERM; 795 goto error;
780 if ((sgid != (gid_t) -1) && (sgid != cred->gid) && 796 if (sgid != (gid_t) -1 && sgid != old->gid &&
781 (sgid != cred->egid) && (sgid != cred->sgid)) 797 sgid != old->egid && sgid != old->sgid)
782 return -EPERM; 798 goto error;
783 } 799 }
784 if (egid != (gid_t) -1) { 800
785 if (egid != cred->egid) {
786 set_dumpable(current->mm, suid_dumpable);
787 smp_wmb();
788 }
789 cred->egid = egid;
790 }
791 cred->fsgid = cred->egid;
792 if (rgid != (gid_t) -1) 801 if (rgid != (gid_t) -1)
793 cred->gid = rgid; 802 new->gid = rgid;
803 if (egid != (gid_t) -1)
804 new->egid = egid;
794 if (sgid != (gid_t) -1) 805 if (sgid != (gid_t) -1)
795 cred->sgid = sgid; 806 new->sgid = sgid;
807 new->fsgid = new->egid;
796 808
797 key_fsgid_changed(current); 809 return commit_creds(new);
798 proc_id_connector(current, PROC_EVENT_GID); 810
799 return 0; 811error:
812 abort_creds(new);
813 return retval;
800} 814}
801 815
802asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid) 816asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid)
@@ -820,28 +834,35 @@ asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __us
820 */ 834 */
821asmlinkage long sys_setfsuid(uid_t uid) 835asmlinkage long sys_setfsuid(uid_t uid)
822{ 836{
823 struct cred *cred = current->cred; 837 const struct cred *old;
824 int old_fsuid; 838 struct cred *new;
839 uid_t old_fsuid;
840
841 new = prepare_creds();
842 if (!new)
843 return current_fsuid();
844 old = current_cred();
845 old_fsuid = old->fsuid;
825 846
826 old_fsuid = cred->fsuid; 847 if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS) < 0)
827 if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS)) 848 goto error;
828 return old_fsuid;
829 849
830 if (uid == cred->uid || uid == cred->euid || 850 if (uid == old->uid || uid == old->euid ||
831 uid == cred->suid || uid == cred->fsuid || 851 uid == old->suid || uid == old->fsuid ||
832 capable(CAP_SETUID)) { 852 capable(CAP_SETUID)) {
833 if (uid != old_fsuid) { 853 if (uid != old_fsuid) {
834 set_dumpable(current->mm, suid_dumpable); 854 new->fsuid = uid;
835 smp_wmb(); 855 if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
856 goto change_okay;
836 } 857 }
837 cred->fsuid = uid;
838 } 858 }
839 859
840 key_fsuid_changed(current); 860error:
841 proc_id_connector(current, PROC_EVENT_UID); 861 abort_creds(new);
842 862 return old_fsuid;
843 security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
844 863
864change_okay:
865 commit_creds(new);
845 return old_fsuid; 866 return old_fsuid;
846} 867}
847 868
@@ -850,24 +871,34 @@ asmlinkage long sys_setfsuid(uid_t uid)
850 */ 871 */
851asmlinkage long sys_setfsgid(gid_t gid) 872asmlinkage long sys_setfsgid(gid_t gid)
852{ 873{
853 struct cred *cred = current->cred; 874 const struct cred *old;
854 int old_fsgid; 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;
855 883
856 old_fsgid = cred->fsgid;
857 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))
858 return old_fsgid; 885 goto error;
859 886
860 if (gid == cred->gid || gid == cred->egid || 887 if (gid == old->gid || gid == old->egid ||
861 gid == cred->sgid || gid == cred->fsgid || 888 gid == old->sgid || gid == old->fsgid ||
862 capable(CAP_SETGID)) { 889 capable(CAP_SETGID)) {
863 if (gid != old_fsgid) { 890 if (gid != old_fsgid) {
864 set_dumpable(current->mm, suid_dumpable); 891 new->fsgid = gid;
865 smp_wmb(); 892 goto change_okay;
866 } 893 }
867 cred->fsgid = gid;
868 key_fsgid_changed(current);
869 proc_id_connector(current, PROC_EVENT_GID);
870 } 894 }
895
896error:
897 abort_creds(new);
898 return old_fsgid;
899
900change_okay:
901 commit_creds(new);
871 return old_fsgid; 902 return old_fsgid;
872} 903}
873 904
@@ -1136,7 +1167,7 @@ EXPORT_SYMBOL(groups_free);
1136 1167
1137/* export the group_info to a user-space array */ 1168/* export the group_info to a user-space array */
1138static int groups_to_user(gid_t __user *grouplist, 1169static int groups_to_user(gid_t __user *grouplist,
1139 struct group_info *group_info) 1170 const struct group_info *group_info)
1140{ 1171{
1141 int i; 1172 int i;
1142 unsigned int count = group_info->ngroups; 1173 unsigned int count = group_info->ngroups;
@@ -1227,31 +1258,25 @@ int groups_search(const struct group_info *group_info, gid_t grp)
1227} 1258}
1228 1259
1229/** 1260/**
1230 * set_groups - Change a group subscription in a security record 1261 * set_groups - Change a group subscription in a set of credentials
1231 * @sec: The security record to alter 1262 * @new: The newly prepared set of credentials to alter
1232 * @group_info: The group list to impose 1263 * @group_info: The group list to install
1233 * 1264 *
1234 * Validate a group subscription and, if valid, impose it upon a task security 1265 * Validate a group subscription and, if valid, insert it into a set
1235 * record. 1266 * of credentials.
1236 */ 1267 */
1237int set_groups(struct cred *cred, struct group_info *group_info) 1268int set_groups(struct cred *new, struct group_info *group_info)
1238{ 1269{
1239 int retval; 1270 int retval;
1240 struct group_info *old_info;
1241 1271
1242 retval = security_task_setgroups(group_info); 1272 retval = security_task_setgroups(group_info);
1243 if (retval) 1273 if (retval)
1244 return retval; 1274 return retval;
1245 1275
1276 put_group_info(new->group_info);
1246 groups_sort(group_info); 1277 groups_sort(group_info);
1247 get_group_info(group_info); 1278 get_group_info(group_info);
1248 1279 new->group_info = group_info;
1249 spin_lock(&cred->lock);
1250 old_info = cred->group_info;
1251 cred->group_info = group_info;
1252 spin_unlock(&cred->lock);
1253
1254 put_group_info(old_info);
1255 return 0; 1280 return 0;
1256} 1281}
1257 1282
@@ -1266,7 +1291,20 @@ EXPORT_SYMBOL(set_groups);
1266 */ 1291 */
1267int set_current_groups(struct group_info *group_info) 1292int set_current_groups(struct group_info *group_info)
1268{ 1293{
1269 return set_groups(current->cred, group_info); 1294 struct cred *new;
1295 int ret;
1296
1297 new = prepare_creds();
1298 if (!new)
1299 return -ENOMEM;
1300
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);
1270} 1308}
1271 1309
1272EXPORT_SYMBOL(set_current_groups); 1310EXPORT_SYMBOL(set_current_groups);
@@ -1666,9 +1704,11 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
1666 unsigned char comm[sizeof(me->comm)]; 1704 unsigned char comm[sizeof(me->comm)];
1667 long error; 1705 long error;
1668 1706
1669 if (security_task_prctl(option, arg2, arg3, arg4, arg5, &error)) 1707 error = security_task_prctl(option, arg2, arg3, arg4, arg5);
1708 if (error != -ENOSYS)
1670 return error; 1709 return error;
1671 1710
1711 error = 0;
1672 switch (option) { 1712 switch (option) {
1673 case PR_SET_PDEATHSIG: 1713 case PR_SET_PDEATHSIG:
1674 if (!valid_signal(arg2)) { 1714 if (!valid_signal(arg2)) {