OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
| 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 "util/mach/task_for_pid.h" |
| 16 |
| 17 #include <sys/sysctl.h> |
| 18 #include <unistd.h> |
| 19 |
| 20 #include <algorithm> |
| 21 #include <iterator> |
| 22 #include <set> |
| 23 |
| 24 #include "base/basictypes.h" |
| 25 #include "base/mac/mach_logging.h" |
| 26 #include "base/mac/scoped_mach_port.h" |
| 27 #include "util/posix/process_info.h" |
| 28 |
| 29 namespace crashpad { |
| 30 |
| 31 namespace { |
| 32 |
| 33 //! \brief Determines whether the groups that \a process_reader belongs to are |
| 34 //! a subset of the groups that the current process belongs to. |
| 35 //! |
| 36 //! This function is similar to 10.9.5 |
| 37 //! `xnu-2422.115.4/bsd/kern/kern_credential.c` `kauth_cred_gid_subset()`. |
| 38 bool TaskForPIDGroupCheck(const ProcessInfo& process_info) { |
| 39 std::set<gid_t> groups = process_info.AllGroups(); |
| 40 |
| 41 ProcessInfo process_info_self; |
| 42 if (!process_info_self.Initialize(getpid())) { |
| 43 return false; |
| 44 } |
| 45 |
| 46 std::set<gid_t> groups_self = process_info_self.AllGroups(); |
| 47 |
| 48 // difference will only contain elements of groups not present in groups_self. |
| 49 // It will not contain elements of groups_self not present in groups. (That |
| 50 // would be std::set_symmetric_difference.) |
| 51 std::set<gid_t> difference; |
| 52 std::set_difference(groups.begin(), |
| 53 groups.end(), |
| 54 groups_self.begin(), |
| 55 groups_self.end(), |
| 56 std::inserter(difference, difference.begin())); |
| 57 if (!difference.empty()) { |
| 58 LOG(ERROR) << "permission denied (gid)"; |
| 59 return false; |
| 60 } |
| 61 |
| 62 return true; |
| 63 } |
| 64 |
| 65 //! \brief Determines whether the current process should have permission to |
| 66 //! access the specified task port. |
| 67 //! |
| 68 //! This function is similar to 10.9.5 |
| 69 //! `xnu-2422.115.4/bsd/vm/vm_unix.c` `task_for_pid_posix_check()`. |
| 70 //! |
| 71 //! This function accepts a `task_t` argument instead of a `pid_t` argument, |
| 72 //! implying that the task send right must be retrieved before it can be |
| 73 //! checked. This is done because a `pid_t` argument may refer to a different |
| 74 //! task in between the time that access is checked and its corresponding |
| 75 //! `task_t` is obtained by `task_for_pid()`. When `task_for_pid()` is called |
| 76 //! first, any operations requiring the process ID will call `pid_for_task()` |
| 77 //! and be guaranteed to use the process ID corresponding to the correct task, |
| 78 //! or to fail if that task is no longer running. If the task dies and the PID |
| 79 //! is recycled, it is still possible to look up the wrong PID, but falsely |
| 80 //! granting task access based on the new process’ characteristics is harmless |
| 81 //! because the task will be a dead name at that point. |
| 82 bool TaskForPIDCheck(task_t task) { |
| 83 // If the effective user ID is not 0, then this code is not running as root at |
| 84 // all, and the kernel’s own checks are sufficient to determine access. The |
| 85 // point of this function is to simulate the kernel’s own checks when the |
| 86 // effective user ID is 0 but the real user ID is anything else. |
| 87 if (geteuid() != 0) { |
| 88 return true; |
| 89 } |
| 90 |
| 91 // If the real user ID is 0, then this code is not running setuid root, it’s |
| 92 // genuinely running as root, and it should be allowed maximum access. |
| 93 uid_t uid = getuid(); |
| 94 if (uid == 0) { |
| 95 return true; |
| 96 } |
| 97 |
| 98 // task_for_pid_posix_check() would permit access to the running process’ own |
| 99 // task here, and would then check the kern.tfp.policy sysctl. If set to |
| 100 // KERN_TFP_POLICY_DENY, it would deny access. |
| 101 // |
| 102 // This behavior is not duplicated here because the point of this function is |
| 103 // to permit task_for_pid() access for setuid root programs. It is assumed |
| 104 // that a setuid root program ought to be able to overcome any policy set in |
| 105 // kern.tfp.policy. |
| 106 // |
| 107 // Access to the running process’ own task is not granted outright and is |
| 108 // instead subjected to the same user/group ID checks as any other process. |
| 109 // This has the effect of denying access to the running process’ own task when |
| 110 // it is setuid root. This is intentional, because it prevents the same sort |
| 111 // of cross-privilege disclosure discussed below at the DidChangePriveleges() |
| 112 // check. The running process can still access its own task port via |
| 113 // mach_task_self(), but a non-root user cannot coerce a setuid root tool to |
| 114 // operate on itself by specifying its own process ID to this TaskForPID() |
| 115 // interface. |
| 116 |
| 117 ProcessInfo process_info; |
| 118 if (!process_info.InitializeFromTask(task)) { |
| 119 return false; |
| 120 } |
| 121 |
| 122 // The target process’ real user ID, effective user ID, and saved set-user ID |
| 123 // must match this process’ own real user ID. task_for_pid_posix_check() |
| 124 // checks against the current process’ effective user ID, but for the purposes |
| 125 // of this function, when running setuid root, the real user ID is the correct |
| 126 // choice. |
| 127 if (process_info.RealUserID() != uid || |
| 128 process_info.EffectiveUserID() != uid || |
| 129 process_info.SavedUserID() != uid) { |
| 130 LOG(ERROR) << "permission denied (uid)"; |
| 131 return false; |
| 132 } |
| 133 |
| 134 // The target process must not have changed privileges. The rationale for this |
| 135 // check is explained in 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c |
| 136 // issetugid(): processes that have changed privileges may have loaded data |
| 137 // using different credentials than they are currently operating with, and |
| 138 // allowing other processes access to this data based solely on a check of the |
| 139 // current credentials could violate confidentiality. |
| 140 if (process_info.DidChangePrivileges()) { |
| 141 LOG(ERROR) << "permission denied (P_SUGID)"; |
| 142 return false; |
| 143 } |
| 144 |
| 145 return TaskForPIDGroupCheck(process_info); |
| 146 } |
| 147 |
| 148 } // namespace |
| 149 |
| 150 task_t TaskForPID(pid_t pid) { |
| 151 task_t task; |
| 152 kern_return_t kr = task_for_pid(mach_task_self(), pid, &task); |
| 153 if (kr != KERN_SUCCESS) { |
| 154 MACH_LOG(ERROR, kr) << "task_for_pid"; |
| 155 return TASK_NULL; |
| 156 } |
| 157 |
| 158 base::mac::ScopedMachSendRight task_owner(task); |
| 159 |
| 160 if (!TaskForPIDCheck(task)) { |
| 161 return TASK_NULL; |
| 162 } |
| 163 |
| 164 return task_owner.release(); |
| 165 } |
| 166 |
| 167 } // namespace crashpad |
OLD | NEW |