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 // different_groups will only contain elements of groups not present in | |
Robert Sesek
2014/11/14 22:50:15
difference
| |
49 // groups_self. It will not contain elements of groups_self not present in | |
50 // groups. (That 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 |