gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/linux_capability_util.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  #ifdef __linux__
    16  
    17  #include "test/util/linux_capability_util.h"
    18  
    19  #include <linux/capability.h>
    20  #include <sched.h>
    21  #include <sys/mman.h>
    22  #include <sys/wait.h>
    23  
    24  #include <iostream>
    25  
    26  #include "absl/strings/str_cat.h"
    27  #include "test/util/memory_util.h"
    28  #include "test/util/posix_error.h"
    29  #include "test/util/save_util.h"
    30  #include "test/util/test_util.h"
    31  
    32  namespace gvisor {
    33  namespace testing {
    34  
    35  PosixErrorOr<bool> HaveRawIPSocketCapability() {
    36    return HaveCapability(CAP_NET_RAW);
    37  }
    38  
    39  PosixErrorOr<bool> HavePacketSocketCapability() {
    40    return HaveCapability(CAP_NET_RAW);
    41  }
    42  
    43  PosixErrorOr<bool> CanCreateUserNamespace() {
    44    // The most reliable way to determine if userns creation is possible is by
    45    // trying to create one; see below.
    46    ASSIGN_OR_RETURN_ERRNO(
    47        auto child_stack,
    48        MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE));
    49    int const child_pid = clone(
    50        +[](void*) { return 0; },
    51        reinterpret_cast<void*>(child_stack.addr() + kPageSize),
    52        CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr);
    53    if (child_pid > 0) {
    54      int status;
    55      int const ret = waitpid(child_pid, &status, /* options = */ 0);
    56      MaybeSave();
    57      if (ret < 0) {
    58        return PosixError(errno, "waitpid");
    59      }
    60      if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    61        return PosixError(
    62            ESRCH, absl::StrCat("child process exited with status ", status));
    63      }
    64      return true;
    65    } else if (errno == EPERM) {
    66      // Per clone(2), EPERM can be returned if:
    67      //
    68      // - "CLONE_NEWUSER was specified in flags, but either the effective user ID
    69      // or the effective group ID of the caller does not have a mapping in the
    70      // parent namespace (see user_namespaces(7))."
    71      //
    72      // - "(since Linux 3.9) CLONE_NEWUSER was specified in flags and the caller
    73      // is in a chroot environment (i.e., the caller's root directory does
    74      // not match the root directory of the mount namespace in which it
    75      // resides)."
    76      std::cerr << "clone(CLONE_NEWUSER) failed with EPERM" << std::endl;
    77      return false;
    78    } else if (errno == EUSERS) {
    79      // "(since Linux 3.11) CLONE_NEWUSER was specified in flags, and the call
    80      // would cause the limit on the number of nested user namespaces to be
    81      // exceeded. See user_namespaces(7)."
    82      std::cerr << "clone(CLONE_NEWUSER) failed with EUSERS" << std::endl;
    83      return false;
    84    } else {
    85      // Unexpected error code; indicate an actual error.
    86      return PosixError(errno, "clone(CLONE_NEWUSER)");
    87    }
    88  }
    89  
    90  }  // namespace testing
    91  }  // namespace gvisor
    92  
    93  #endif  // __linux__