|
16 | 16 |
|
17 | 17 | #include <errno.h> |
18 | 18 | #include <net/if.h> |
19 | | -#include <stdio.h> |
20 | 19 | #include <sys/ioctl.h> |
21 | 20 | #include <sys/socket.h> |
22 | 21 | #include <sys/types.h> |
23 | 22 | #include <sys/un.h> |
| 23 | +#include <sys/wait.h> |
| 24 | +#include <unistd.h> |
24 | 25 |
|
25 | 26 | #include <vector> |
26 | 27 |
|
27 | 28 | #include "gtest/gtest.h" |
28 | | -#include "absl/strings/string_view.h" |
29 | 29 | #include "test/syscalls/linux/unix_domain_socket_test_util.h" |
| 30 | +#include "test/util/cleanup.h" |
| 31 | +#include "test/util/linux_capability_util.h" |
30 | 32 | #include "test/util/socket_util.h" |
31 | 33 | #include "test/util/test_util.h" |
32 | 34 | #include "test/util/thread_util.h" |
@@ -722,6 +724,150 @@ TEST_P(UnixSocketPairCmsgTest, BasicCredPass) { |
722 | 724 | EXPECT_EQ(sent_creds.gid, received_creds.gid); |
723 | 725 | } |
724 | 726 |
|
| 727 | +// A privileged sender (CAP_SYS_ADMIN) may attach SCM_CREDENTIALS naming the PID |
| 728 | +// of a different, live process. The receiver must observe that process's PID. |
| 729 | +TEST_P(UnixSocketPairCmsgTest, BasicCredPassDifferentPID) { |
| 730 | + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); |
| 731 | + |
| 732 | + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); |
| 733 | + |
| 734 | + // Pipe to signal to child when to exit. |
| 735 | + int pipefd[2]; |
| 736 | + ASSERT_THAT(pipe(pipefd), SyscallSucceeds()); |
| 737 | + |
| 738 | + pid_t child = fork(); |
| 739 | + if (child == 0) { |
| 740 | + close(pipefd[1]); |
| 741 | + char c; |
| 742 | + RetryEINTR(read)(pipefd[0], &c, 1); |
| 743 | + _exit(0); |
| 744 | + } |
| 745 | + ASSERT_THAT(child, SyscallSucceeds()); |
| 746 | + ASSERT_THAT(close(pipefd[0]), SyscallSucceeds()); |
| 747 | + |
| 748 | + auto cleanup = Cleanup([&child, &pipefd] { |
| 749 | + ASSERT_THAT(close(pipefd[1]), SyscallSucceeds()); |
| 750 | + ASSERT_THAT(RetryEINTR(waitpid)(child, nullptr, 0), |
| 751 | + SyscallSucceedsWithValue(child)); |
| 752 | + }); |
| 753 | + |
| 754 | + char sent_data[20]; |
| 755 | + RandomizeBuffer(sent_data, sizeof(sent_data)); |
| 756 | + |
| 757 | + struct ucred sent_creds; |
| 758 | + sent_creds.pid = child; |
| 759 | + ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); |
| 760 | + ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); |
| 761 | + |
| 762 | + ASSERT_NO_FATAL_FAILURE( |
| 763 | + SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); |
| 764 | + |
| 765 | + SetSoPassCred(sockets->second_fd()); |
| 766 | + |
| 767 | + char received_data[20]; |
| 768 | + struct ucred received_creds; |
| 769 | + ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, |
| 770 | + received_data, sizeof(received_data))); |
| 771 | + |
| 772 | + EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); |
| 773 | + // The received PID must be the child's, not the sender's. |
| 774 | + EXPECT_EQ(received_creds.pid, child); |
| 775 | + EXPECT_NE(received_creds.pid, getpid()); |
| 776 | + EXPECT_EQ(received_creds.uid, sent_creds.uid); |
| 777 | + EXPECT_EQ(received_creds.gid, sent_creds.gid); |
| 778 | +} |
| 779 | + |
| 780 | +// A sender without CAP_SYS_ADMIN should not be able to send SCM_CREDENTIALS |
| 781 | +// messages with a different PID. |
| 782 | +TEST_P(UnixSocketPairCmsgTest, CredPassDifferentPIDUnprivileged) { |
| 783 | + // Drop CAP_SYS_ADMIN |
| 784 | + AutoCapability cap(CAP_SYS_ADMIN, false); |
| 785 | + |
| 786 | + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); |
| 787 | + |
| 788 | + // Pipe to signal to child when to exit. |
| 789 | + int pipefd[2]; |
| 790 | + ASSERT_THAT(pipe(pipefd), SyscallSucceeds()); |
| 791 | + |
| 792 | + pid_t child = fork(); |
| 793 | + if (child == 0) { |
| 794 | + close(pipefd[1]); |
| 795 | + char c; |
| 796 | + RetryEINTR(read)(pipefd[0], &c, 1); |
| 797 | + _exit(0); |
| 798 | + } |
| 799 | + ASSERT_THAT(child, SyscallSucceeds()); |
| 800 | + ASSERT_THAT(close(pipefd[0]), SyscallSucceeds()); |
| 801 | + |
| 802 | + auto cleanup = Cleanup([&child, &pipefd] { |
| 803 | + ASSERT_THAT(close(pipefd[1]), SyscallSucceeds()); |
| 804 | + ASSERT_THAT(RetryEINTR(waitpid)(child, nullptr, 0), |
| 805 | + SyscallSucceedsWithValue(child)); |
| 806 | + }); |
| 807 | + |
| 808 | + char sent_data[20]; |
| 809 | + RandomizeBuffer(sent_data, sizeof(sent_data)); |
| 810 | + |
| 811 | + struct ucred sent_creds; |
| 812 | + sent_creds.pid = child; |
| 813 | + ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); |
| 814 | + ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); |
| 815 | + struct msghdr msg = {}; |
| 816 | + char control[CMSG_SPACE(sizeof(struct ucred))]; |
| 817 | + msg.msg_control = control; |
| 818 | + msg.msg_controllen = sizeof(control); |
| 819 | + |
| 820 | + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); |
| 821 | + cmsg->cmsg_level = SOL_SOCKET; |
| 822 | + cmsg->cmsg_type = SCM_CREDENTIALS; |
| 823 | + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); |
| 824 | + memcpy(CMSG_DATA(cmsg), &sent_creds, sizeof(struct ucred)); |
| 825 | + |
| 826 | + struct iovec iov; |
| 827 | + iov.iov_base = sent_data; |
| 828 | + iov.iov_len = sizeof(sent_data); |
| 829 | + msg.msg_iov = &iov; |
| 830 | + msg.msg_iovlen = 1; |
| 831 | + |
| 832 | + ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), |
| 833 | + SyscallFailsWithErrno(EPERM)); |
| 834 | +} |
| 835 | + |
| 836 | +// Sending SCM_CREDENTIALS naming a PID that does not exist fails with ESRCH. |
| 837 | +TEST_P(UnixSocketPairCmsgTest, CredPassNonexistentPID) { |
| 838 | + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); |
| 839 | + |
| 840 | + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); |
| 841 | + |
| 842 | + char sent_data[20]; |
| 843 | + RandomizeBuffer(sent_data, sizeof(sent_data)); |
| 844 | + |
| 845 | + struct ucred sent_creds; |
| 846 | + sent_creds.pid = -1; |
| 847 | + ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); |
| 848 | + ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); |
| 849 | + |
| 850 | + struct msghdr msg = {}; |
| 851 | + char control[CMSG_SPACE(sizeof(struct ucred))]; |
| 852 | + msg.msg_control = control; |
| 853 | + msg.msg_controllen = sizeof(control); |
| 854 | + |
| 855 | + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); |
| 856 | + cmsg->cmsg_level = SOL_SOCKET; |
| 857 | + cmsg->cmsg_type = SCM_CREDENTIALS; |
| 858 | + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); |
| 859 | + memcpy(CMSG_DATA(cmsg), &sent_creds, sizeof(struct ucred)); |
| 860 | + |
| 861 | + struct iovec iov; |
| 862 | + iov.iov_base = sent_data; |
| 863 | + iov.iov_len = sizeof(sent_data); |
| 864 | + msg.msg_iov = &iov; |
| 865 | + msg.msg_iovlen = 1; |
| 866 | + |
| 867 | + ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), |
| 868 | + SyscallFailsWithErrno(ESRCH)); |
| 869 | +} |
| 870 | + |
725 | 871 | TEST_P(UnixSocketPairCmsgTest, SendNullCredsBeforeSoPassCredRecvEnd) { |
726 | 872 | auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); |
727 | 873 |
|
|
0 commit comments