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/compiler_specific.h"
     14 #include "base/posix/eintr_wrapper.h"
     15 #include "sandbox/linux/services/scoped_process.h"
     16 #include "sandbox/linux/services/yama.h"
     17 #include "sandbox/linux/tests/unit_tests.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 namespace sandbox {
     21 
     22 namespace {
     23 
     24 bool CanPtrace(pid_t pid) {
     25   int ret;
     26   ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
     27   if (ret == -1) {
     28     CHECK_EQ(EPERM, errno);
     29     return false;
     30   }
     31   // Wait for the process to be stopped so that it can be detached.
     32   siginfo_t process_info;
     33   int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
     34   PCHECK(0 == wait_ret);
     35   PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
     36   return true;
     37 }
     38 
     39 // _exit(0) if pid can be ptraced by the current process.
     40 // _exit(1) otherwise.
     41 void ExitZeroIfCanPtrace(pid_t pid) {
     42   if (CanPtrace(pid)) {
     43     _exit(0);
     44   } else {
     45     _exit(1);
     46   }
     47 }
     48 
     49 bool CanSubProcessPtrace(pid_t pid) {
     50   ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
     51   bool signaled;
     52   int exit_code = process.WaitForExit(&signaled);
     53   CHECK(!signaled);
     54   return 0 == exit_code;
     55 }
     56 
     57 // The tests below assume that the system-level configuration will not change
     58 // while they run.
     59 
     60 TEST(Yama, GetStatus) {
     61   int status1 = Yama::GetStatus();
     62 
     63   // Check that the value is a possible bitmask.
     64   ASSERT_LE(0, status1);
     65   ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
     66                 Yama::STATUS_STRICT_ENFORCING,
     67             status1);
     68 
     69   // The status should not just be a random value.
     70   int status2 = Yama::GetStatus();
     71   EXPECT_EQ(status1, status2);
     72 
     73   // This test is not running sandboxed, there is no reason to not know the
     74   // status.
     75   EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
     76 
     77   if (status1 & Yama::STATUS_STRICT_ENFORCING) {
     78     // If Yama is strictly enforcing, it is also enforcing.
     79     EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
     80   }
     81 
     82   if (status1 & Yama::STATUS_ENFORCING) {
     83     // If Yama is enforcing, Yama is present.
     84     EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
     85   }
     86 
     87   // Verify that the helper functions work as intended.
     88   EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
     89             Yama::IsEnforcing());
     90   EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
     91             Yama::IsPresent());
     92 
     93   fprintf(stdout,
     94           "Yama present: %s - enforcing: %s\n",
     95           Yama::IsPresent() ? "Y" : "N",
     96           Yama::IsEnforcing() ? "Y" : "N");
     97 }
     98 
     99 SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
    100   // This call will succeed iff Yama is present.
    101   bool restricted = Yama::RestrictPtracersToAncestors();
    102   CHECK_EQ(restricted, Yama::IsPresent());
    103 }
    104 
    105 // Attempts to enable or disable Yama restrictions.
    106 void SetYamaRestrictions(bool enable_restriction) {
    107   if (enable_restriction) {
    108     Yama::RestrictPtracersToAncestors();
    109   } else {
    110     Yama::DisableYamaRestrictions();
    111   }
    112 }
    113 
    114 TEST(Yama, RestrictPtraceWorks) {
    115   ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
    116   ASSERT_TRUE(process1.WaitForClosureToRun());
    117 
    118   if (Yama::IsEnforcing()) {
    119     // A sibling process cannot ptrace process1.
    120     ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
    121   }
    122 
    123   if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
    124     // However, parent can ptrace process1.
    125     ASSERT_TRUE(CanPtrace(process1.GetPid()));
    126 
    127     // A sibling can ptrace process2 which disables any Yama protection.
    128     ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
    129     ASSERT_TRUE(process2.WaitForClosureToRun());
    130     ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
    131   }
    132 }
    133 
    134 void DoNothing() {}
    135 
    136 SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
    137   if (!Yama::IsPresent())
    138     return;
    139 
    140   CHECK(Yama::DisableYamaRestrictions());
    141   ScopedProcess process1(base::Bind(&DoNothing));
    142 
    143   if (Yama::IsEnforcing()) {
    144     // Check that process1 is protected by Yama, even though it has
    145     // been created from a process that disabled Yama.
    146     CHECK(!CanSubProcessPtrace(process1.GetPid()));
    147   }
    148 }
    149 
    150 }  // namespace
    151 
    152 }  // namespace sandbox
    153