Home | History | Annotate | Download | only in services
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <sys/ptrace.h>
      8 #include <sys/stat.h>
      9 #include <sys/types.h>
     10 #include <unistd.h>
     11 
     12 #include "base/bind.h"
     13 #include "base/bind_helpers.h"
     14 #include "base/compiler_specific.h"
     15 #include "base/posix/eintr_wrapper.h"
     16 #include "base/strings/string_util.h"
     17 #include "base/sys_info.h"
     18 #include "sandbox/linux/services/scoped_process.h"
     19 #include "sandbox/linux/services/yama.h"
     20 #include "sandbox/linux/tests/unit_tests.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 
     23 namespace sandbox {
     24 
     25 namespace {
     26 
     27 bool HasLinux32Bug() {
     28 #if defined(__i386__)
     29   // On 3.2 kernels, yama doesn't work for 32-bit binaries on 64-bit kernels.
     30   // This is fixed in 3.4.
     31   bool is_kernel_64bit =
     32       base::SysInfo::OperatingSystemArchitecture() == "x86_64";
     33   bool is_linux = base::SysInfo::OperatingSystemName() == "Linux";
     34   bool is_3_dot_2 = base::StartsWith(
     35       base::SysInfo::OperatingSystemVersion(), "3.2",
     36       base::CompareCase::INSENSITIVE_ASCII);
     37   if (is_kernel_64bit && is_linux && is_3_dot_2)
     38     return true;
     39 #endif  // defined(__i386__)
     40   return false;
     41 }
     42 
     43 bool CanPtrace(pid_t pid) {
     44   int ret;
     45   ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
     46   if (ret == -1) {
     47     CHECK_EQ(EPERM, errno);
     48     return false;
     49   }
     50   // Wait for the process to be stopped so that it can be detached.
     51   siginfo_t process_info;
     52   int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
     53   PCHECK(0 == wait_ret);
     54   PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
     55   return true;
     56 }
     57 
     58 // _exit(0) if pid can be ptraced by the current process.
     59 // _exit(1) otherwise.
     60 void ExitZeroIfCanPtrace(pid_t pid) {
     61   if (CanPtrace(pid)) {
     62     _exit(0);
     63   } else {
     64     _exit(1);
     65   }
     66 }
     67 
     68 bool CanSubProcessPtrace(pid_t pid) {
     69   ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
     70   bool signaled;
     71   int exit_code = process.WaitForExit(&signaled);
     72   CHECK(!signaled);
     73   return 0 == exit_code;
     74 }
     75 
     76 // The tests below assume that the system-level configuration will not change
     77 // while they run.
     78 
     79 TEST(Yama, GetStatus) {
     80   int status1 = Yama::GetStatus();
     81 
     82   // Check that the value is a possible bitmask.
     83   ASSERT_LE(0, status1);
     84   ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
     85                 Yama::STATUS_STRICT_ENFORCING,
     86             status1);
     87 
     88   // The status should not just be a random value.
     89   int status2 = Yama::GetStatus();
     90   EXPECT_EQ(status1, status2);
     91 
     92   // This test is not running sandboxed, there is no reason to not know the
     93   // status.
     94   EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
     95 
     96   if (status1 & Yama::STATUS_STRICT_ENFORCING) {
     97     // If Yama is strictly enforcing, it is also enforcing.
     98     EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
     99   }
    100 
    101   if (status1 & Yama::STATUS_ENFORCING) {
    102     // If Yama is enforcing, Yama is present.
    103     EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
    104   }
    105 
    106   // Verify that the helper functions work as intended.
    107   EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
    108             Yama::IsEnforcing());
    109   EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
    110             Yama::IsPresent());
    111 
    112   fprintf(stdout,
    113           "Yama present: %s - enforcing: %s\n",
    114           Yama::IsPresent() ? "Y" : "N",
    115           Yama::IsEnforcing() ? "Y" : "N");
    116 }
    117 
    118 SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
    119   // This call will succeed iff Yama is present.
    120   bool restricted = Yama::RestrictPtracersToAncestors();
    121   CHECK_EQ(restricted, Yama::IsPresent());
    122 }
    123 
    124 // Attempts to enable or disable Yama restrictions.
    125 void SetYamaRestrictions(bool enable_restriction) {
    126   if (enable_restriction) {
    127     Yama::RestrictPtracersToAncestors();
    128   } else {
    129     Yama::DisableYamaRestrictions();
    130   }
    131 }
    132 
    133 TEST(Yama, RestrictPtraceWorks) {
    134   if (HasLinux32Bug())
    135     return;
    136 
    137   ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
    138   ASSERT_TRUE(process1.WaitForClosureToRun());
    139 
    140   if (Yama::IsEnforcing()) {
    141     // A sibling process cannot ptrace process1.
    142     ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
    143   }
    144 
    145   if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
    146     // However, parent can ptrace process1.
    147     ASSERT_TRUE(CanPtrace(process1.GetPid()));
    148 
    149     // A sibling can ptrace process2 which disables any Yama protection.
    150     ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
    151     ASSERT_TRUE(process2.WaitForClosureToRun());
    152     ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
    153   }
    154 }
    155 
    156 SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
    157   if (!Yama::IsPresent() || HasLinux32Bug())
    158     return;
    159 
    160   CHECK(Yama::DisableYamaRestrictions());
    161   ScopedProcess process1(base::Bind(&base::DoNothing));
    162 
    163   if (Yama::IsEnforcing()) {
    164     // Check that process1 is protected by Yama, even though it has
    165     // been created from a process that disabled Yama.
    166     CHECK(!CanSubProcessPtrace(process1.GetPid()));
    167   }
    168 }
    169 
    170 }  // namespace
    171 
    172 }  // namespace sandbox
    173