When a task is writing to an fd opened by a different task, the perm check should use the credentials of the latter task. Add a test for it.
Unified · Split
| @@ -674,6 +674,73 @@ cleanup: · 673 unmodified lines | |||
| 674 | return ret; | 674 | return ret; |
| 675 | } | 675 | } |
| 676 | 676 | ||
| 677 | /* | ||
| 678 | * cgroup migration permission check should be performed based on the | ||
| 679 | * credentials at the time of open instead of write. | ||
| 680 | */ | ||
| 681 | static int test_cgcore_lesser_euid_open(const char *root) | ||
| 682 | { | ||
| 683 | const uid_t test_euid = 65534; /* usually nobody, any !root is fine */ | ||
| 684 | int ret = KSFT_FAIL; | ||
| 685 | char *cg_test_a = NULL, *cg_test_b = NULL; | ||
| 686 | char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL; | ||
| 687 | int cg_test_b_procs_fd = -1; | ||
| 688 | uid_t saved_uid; | ||
| 689 | |||
| 690 | cg_test_a = cg_name(root, "cg_test_a"); | ||
| 691 | cg_test_b = cg_name(root, "cg_test_b"); | ||
| 692 | |||
| 693 | if (!cg_test_a || !cg_test_b) | ||
| 694 | goto cleanup; | ||
| 695 | |||
| 696 | cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs"); | ||
| 697 | cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs"); | ||
| 698 | |||
| 699 | if (!cg_test_a_procs || !cg_test_b_procs) | ||
| 700 | goto cleanup; | ||
| 701 | |||
| 702 | if (cg_create(cg_test_a) || cg_create(cg_test_b)) | ||
| 703 | goto cleanup; | ||
| 704 | |||
| 705 | if (cg_enter_current(cg_test_a)) | ||
| 706 | goto cleanup; | ||
| 707 | |||
| 708 | if (chown(cg_test_a_procs, test_euid, -1) || | ||
| 709 | chown(cg_test_b_procs, test_euid, -1)) | ||
| 710 | goto cleanup; | ||
| 711 | |||
| 712 | saved_uid = geteuid(); | ||
| 713 | if (seteuid(test_euid)) | ||
| 714 | goto cleanup; | ||
| 715 | |||
| 716 | cg_test_b_procs_fd = open(cg_test_b_procs, O_RDWR); | ||
| 717 | |||
| 718 | if (seteuid(saved_uid)) | ||
| 719 | goto cleanup; | ||
| 720 | |||
| 721 | if (cg_test_b_procs_fd < 0) | ||
| 722 | goto cleanup; | ||
| 723 | |||
| 724 | if (write(cg_test_b_procs_fd, "0", 1) >= 0 || errno != EACCES) | ||
| 725 | goto cleanup; | ||
| 726 | |||
| 727 | ret = KSFT_PASS; | ||
| 728 | |||
| 729 | cleanup: | ||
| 730 | cg_enter_current(root); | ||
| 731 | if (cg_test_b_procs_fd >= 0) | ||
| 732 | close(cg_test_b_procs_fd); | ||
| 733 | if (cg_test_b) | ||
| 734 | cg_destroy(cg_test_b); | ||
| 735 | if (cg_test_a) | ||
| 736 | cg_destroy(cg_test_a); | ||
| 737 | free(cg_test_b_procs); | ||
| 738 | free(cg_test_a_procs); | ||
| 739 | free(cg_test_b); | ||
| 740 | free(cg_test_a); | ||
| 741 | return ret; | ||
| 742 | } | ||
| 743 | |||
| 677 | #define T(x) { x, #x } | 744 | #define T(x) { x, #x } |
| 678 | struct corecg_test { | 745 | struct corecg_test { |
| 679 | int (*fn)(const char *root); | 746 | int (*fn)(const char *root); |
| @@ -689,6 +756,7 @@ struct corecg_test { · 9 unmodified lines | |||
| 689 | T(test_cgcore_proc_migration), | 756 | T(test_cgcore_proc_migration), |
| 690 | T(test_cgcore_thread_migration), | 757 | T(test_cgcore_thread_migration), |
| 691 | T(test_cgcore_destroy), | 758 | T(test_cgcore_destroy), |
| 759 | T(test_cgcore_lesser_euid_open), | ||
| 692 | }; | 760 | }; |
| 693 | #undef T | 761 | #undef T |
| 694 | 762 | ||