github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/prctl_setuid.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <sched.h> 16 #include <sys/prctl.h> 17 18 #include <string> 19 20 #include "gtest/gtest.h" 21 #include "absl/flags/flag.h" 22 #include "test/util/capability_util.h" 23 #include "test/util/logging.h" 24 #include "test/util/multiprocess_util.h" 25 #include "test/util/posix_error.h" 26 #include "test/util/test_util.h" 27 #include "test/util/thread_util.h" 28 29 ABSL_FLAG(int32_t, scratch_uid, 65534, "scratch UID"); 30 // This flag is used to verify that after an exec PR_GET_KEEPCAPS 31 // returns 0, the return code will be offset by kPrGetKeepCapsExitBase. 32 ABSL_FLAG(bool, prctl_pr_get_keepcaps, false, 33 "If true the test will verify that prctl with pr_get_keepcaps" 34 "returns 0. The test will exit with the result of that check."); 35 36 // These tests exist seperately from prctl because we need to start 37 // them as root. Setuid() has the behavior that permissions are fully 38 // removed if one of the UIDs were 0 before a setuid() call. This 39 // behavior can be changed by using PR_SET_KEEPCAPS and that is what 40 // is tested here. 41 // 42 // Reference setuid(2): 43 // The setuid() function checks the effective user ID of 44 // the caller and if it is the superuser, all process-related user ID's 45 // are set to uid. After this has occurred, it is impossible for the 46 // program to regain root privileges. 47 // 48 // Thus, a set-user-ID-root program wishing to temporarily drop root 49 // privileges, assume the identity of an unprivileged user, and then 50 // regain root privileges afterward cannot use setuid(). You can 51 // accomplish this with seteuid(2). 52 namespace gvisor { 53 namespace testing { 54 55 // Offset added to exit code from test child to distinguish from other abnormal 56 // exits. 57 constexpr int kPrGetKeepCapsExitBase = 100; 58 59 namespace { 60 61 class PrctlKeepCapsSetuidTest : public ::testing::Test { 62 protected: 63 void SetUp() override { 64 // PR_GET_KEEPCAPS will only return 0 or 1 (on success). 65 ASSERT_THAT(original_keepcaps_ = prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), 66 SyscallSucceeds()); 67 ASSERT_TRUE(original_keepcaps_ == 0 || original_keepcaps_ == 1); 68 } 69 70 void TearDown() override { 71 // Restore PR_SET_KEEPCAPS. 72 ASSERT_THAT(prctl(PR_SET_KEEPCAPS, original_keepcaps_, 0, 0, 0), 73 SyscallSucceeds()); 74 75 // Verify that it was restored. 76 ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), 77 SyscallSucceedsWithValue(original_keepcaps_)); 78 } 79 80 // The original keep caps value exposed so tests can use it if they need. 81 int original_keepcaps_ = 0; 82 }; 83 84 // This test will verify that a bad value, eg. not 0 or 1 for 85 // PR_SET_KEEPCAPS will return EINVAL as required by prctl(2). 86 TEST_F(PrctlKeepCapsSetuidTest, PrctlBadArgsToKeepCaps) { 87 ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 2, 0, 0, 0), 88 SyscallFailsWithErrno(EINVAL)); 89 } 90 91 // This test will verify that a setuid(2) without PR_SET_KEEPCAPS will cause 92 // all capabilities to be dropped. 93 TEST_F(PrctlKeepCapsSetuidTest, SetUidNoKeepCaps) { 94 // getuid(2) never fails. 95 if (getuid() != 0) { 96 SKIP_IF(!IsRunningOnGvisor()); 97 FAIL() << "User is not root on gvisor platform."; 98 } 99 100 // Do setuid in a separate thread so that after finishing this test, the 101 // process can still open files the test harness created before starting 102 // this test. Otherwise, the files are created by root (UID before the 103 // test), but cannot be opened by the `uid` set below after the test. After 104 // calling setuid(non-zero-UID), there is no way to get root privileges 105 // back. 106 ScopedThread([] { 107 // Start by verifying we have a capability. 108 TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); 109 110 // Verify that PR_GET_KEEPCAPS is disabled. 111 ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), 112 SyscallSucceedsWithValue(0)); 113 114 // Use syscall instead of glibc setuid wrapper because we want this setuid 115 // call to only apply to this task. POSIX threads, however, require that 116 // all threads have the same UIDs, so using the setuid wrapper sets all 117 // threads' real UID. 118 EXPECT_THAT(syscall(SYS_setuid, absl::GetFlag(FLAGS_scratch_uid)), 119 SyscallSucceeds()); 120 121 // Verify that we changed uid. 122 EXPECT_THAT(getuid(), 123 SyscallSucceedsWithValue(absl::GetFlag(FLAGS_scratch_uid))); 124 125 // Verify we lost the capability in the effective set, this always happens. 126 TEST_CHECK(!HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); 127 128 // We should have also lost it in the permitted set by the setuid() so 129 // SetCapability should fail when we try to add it back to the effective set 130 ASSERT_FALSE(SetCapability(CAP_SYS_ADMIN, true).ok()); 131 }); 132 } 133 134 // This test will verify that a setuid with PR_SET_KEEPCAPS will cause 135 // capabilities to be retained after we switch away from the root user. 136 TEST_F(PrctlKeepCapsSetuidTest, SetUidKeepCaps) { 137 // getuid(2) never fails. 138 if (getuid() != 0) { 139 SKIP_IF(!IsRunningOnGvisor()); 140 FAIL() << "User is not root on gvisor platform."; 141 } 142 143 // Do setuid in a separate thread so that after finishing this test, the 144 // process can still open files the test harness created before starting 145 // this test. Otherwise, the files are created by root (UID before the 146 // test), but cannot be opened by the `uid` set below after the test. After 147 // calling setuid(non-zero-UID), there is no way to get root privileges 148 // back. 149 ScopedThread([] { 150 // Start by verifying we have a capability. 151 TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); 152 153 // Set PR_SET_KEEPCAPS. 154 ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); 155 156 // Verify PR_SET_KEEPCAPS was set before we proceed. 157 ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), 158 SyscallSucceedsWithValue(1)); 159 160 // Use syscall instead of glibc setuid wrapper because we want this setuid 161 // call to only apply to this task. POSIX threads, however, require that 162 // all threads have the same UIDs, so using the setuid wrapper sets all 163 // threads' real UID. 164 EXPECT_THAT(syscall(SYS_setuid, absl::GetFlag(FLAGS_scratch_uid)), 165 SyscallSucceeds()); 166 167 // Verify that we changed uid. 168 EXPECT_THAT(getuid(), 169 SyscallSucceedsWithValue(absl::GetFlag(FLAGS_scratch_uid))); 170 171 // Verify we lost the capability in the effective set, this always happens. 172 TEST_CHECK(!HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); 173 174 // We lost the capability in the effective set, but it will still 175 // exist in the permitted set so we can elevate the capability. 176 ASSERT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, true)); 177 178 // Verify we got back the capability in the effective set. 179 TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); 180 }); 181 } 182 183 // This test will verify that PR_SET_KEEPCAPS is not retained 184 // across an execve. According to prctl(2): 185 // "The "keep capabilities" value will be reset to 0 on subsequent 186 // calls to execve(2)." 187 TEST_F(PrctlKeepCapsSetuidTest, NoKeepCapsAfterExec) { 188 ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); 189 190 // Verify PR_SET_KEEPCAPS was set before we proceed. 191 ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), SyscallSucceedsWithValue(1)); 192 193 pid_t child_pid = -1; 194 int execve_errno = 0; 195 // Do an exec and then verify that PR_GET_KEEPCAPS returns 0 196 // see the body of main below. 197 auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( 198 "/proc/self/exe", {"/proc/self/exe", "--prctl_pr_get_keepcaps"}, {}, 199 nullptr, &child_pid, &execve_errno)); 200 201 ASSERT_GT(child_pid, 0); 202 ASSERT_EQ(execve_errno, 0); 203 204 int status = 0; 205 ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); 206 ASSERT_TRUE(WIFEXITED(status)); 207 // PR_SET_KEEPCAPS should have been cleared by the exec. 208 // Success should return gvisor::testing::kPrGetKeepCapsExitBase + 0 209 ASSERT_EQ(WEXITSTATUS(status), kPrGetKeepCapsExitBase); 210 } 211 212 TEST_F(PrctlKeepCapsSetuidTest, NoKeepCapsAfterNewUserNamespace) { 213 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); 214 215 // Fork to avoid changing the user namespace of the original test process. 216 pid_t const child_pid = fork(); 217 218 if (child_pid == 0) { 219 // Verify that the keepcaps flag is set to 0 when we change user namespaces. 220 TEST_PCHECK(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0); 221 MaybeSave(); 222 223 TEST_PCHECK(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0) == 1); 224 MaybeSave(); 225 226 TEST_PCHECK(unshare(CLONE_NEWUSER) == 0); 227 MaybeSave(); 228 229 TEST_PCHECK(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0) == 0); 230 MaybeSave(); 231 232 _exit(0); 233 } 234 235 int status; 236 ASSERT_THAT(child_pid, SyscallSucceeds()); 237 ASSERT_THAT(waitpid(child_pid, &status, 0), 238 SyscallSucceedsWithValue(child_pid)); 239 EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) 240 << "status = " << status; 241 } 242 243 // This test will verify that PR_SET_KEEPCAPS and PR_GET_KEEPCAPS work correctly 244 TEST_F(PrctlKeepCapsSetuidTest, PrGetKeepCaps) { 245 // Set PR_SET_KEEPCAPS to the negation of the original. 246 ASSERT_THAT(prctl(PR_SET_KEEPCAPS, !original_keepcaps_, 0, 0, 0), 247 SyscallSucceeds()); 248 249 // Verify it was set. 250 ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), 251 SyscallSucceedsWithValue(!original_keepcaps_)); 252 } 253 254 } // namespace 255 256 } // namespace testing 257 } // namespace gvisor 258 259 int main(int argc, char** argv) { 260 gvisor::testing::TestInit(&argc, &argv); 261 262 if (absl::GetFlag(FLAGS_prctl_pr_get_keepcaps)) { 263 return gvisor::testing::kPrGetKeepCapsExitBase + 264 prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0); 265 } 266 267 return gvisor::testing::RunAllTests(); 268 }