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 #ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_ | |
16 #define CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_ | |
17 | |
18 #include <mach/mach.h> | |
19 | |
20 #include <string> | |
21 | |
22 #include "base/basictypes.h" | |
23 #include "base/files/scoped_file.h" | |
24 #include "util/mach/child_port_server.h" | |
25 | |
26 namespace crashpad { | |
27 | |
28 //! \brief Implements a handshake protocol that allows a parent process to | |
29 //! obtain a Mach port right from a child process. | |
30 //! | |
31 //! Ordinarily, there is no way for parent and child processes to exchange port | |
32 //! rights, outside of the rights that children inherit from their parents. | |
33 //! These include task-special ports and exception ports, but all of these have | |
34 //! system-defined uses, and cannot reliably be replaced: in a multi-threaded | |
35 //! parent, it is impossible to temporarily change one an inheritable port while | |
36 //! maintaining a guarantee that another thread will not attempt to use it, and | |
37 //! in children, it difficult to guarantee that nothing will attempt to use an | |
38 //! inheritable port before it can be replaced with the correct one. This latter | |
39 //! concern is becoming increasingly more pronounced as system libraries perform | |
40 //! more operations that rely on an inheritable port in module initializers. | |
41 //! | |
42 //! The protocol implemented by this class involves a server that runs in the | |
43 //! parent process. The server is published with the bootstrap server, which the | |
44 //! child has access to because the bootstrap port is one of the inherited | |
45 //! task-special ports. The parent and child also share a pipe, which the parent | |
46 //! can write to and the child can read from. After launching a child process, | |
47 //! the parent will write a random token to this pipe, along with the name under | |
48 //! which its server has been registered with the bootstrap server. The child | |
49 //! can then obtain a send right to this server with `bootstrap_look_up()`, and | |
50 //! send a check-in message containing the token value and the port right of its | |
51 //! choice by calling `child_port_check_in()`. | |
52 //! | |
53 //! The inclusion of the token authenticates the child to its parent. This is | |
54 //! necessary because the service is published with the bootstrap server, which | |
55 //! opens up access to it to more than the child process. Because the token is | |
56 //! passed to the child by a shared pipe, it constitutes a shared secret not | |
57 //! known by other processes that may have incidental access to the server. This | |
58 //! mechanism is used instead of examining the request message’s audit trailer | |
59 //! to verify the sender’s process ID because in some process architectures, it | |
60 //! may be impossible to verify the child’s process ID. This may happen when the | |
61 //! child disassociates from the parent with a double fork(), and the actual | |
Robert Sesek
2014/11/25 18:29:27
For how long is the token valid? If a process does
| |
62 //! client is the parent’s grandchild. | |
63 //! | |
64 //! The shared pipe serves another purpose: the server monitors it for an | |
65 //! end-of-file (no readers) condition . Once detected, it will stop its | |
Robert Sesek
2014/11/25 18:29:27
nit: space before .
| |
66 //! blocking wait for a client to check in. This mechanism was chosen over | |
67 //! monitoring a child process directly for exit to account for the possibility | |
68 //! that the child might disassociate with a double fork(). | |
69 //! | |
70 //! This class can be used to allow a child process to provide its parent with | |
71 //! a send right to its task port, in cases where it is desirable for the parent | |
72 //! to have such access. It can also be used to allow a child process to | |
73 //! establish its own server and provide its parent with a send right to that | |
74 //! server, for cases where a service is provided and it is undesirable or | |
75 //! impossible to provide it via the bootstrap or launchd interfaces. | |
76 class ChildPortHandshake : public ChildPortServer::Interface { | |
77 public: | |
78 //! \brief Initializes the server. | |
79 //! | |
80 //! This creates the pipe so that the “read” side can be obtained by calling | |
81 //! ReadPipeFD(). | |
82 ChildPortHandshake(); | |
83 | |
84 ~ChildPortHandshake(); | |
Robert Sesek
2014/11/25 18:29:26
override ?
Mark Mentovai
2014/11/25 19:20:29
Robert Sesek wrote:
| |
85 | |
86 //! \brief Obtains the “read” side of the pipe, to be used by the client. | |
87 //! | |
88 //! Callers must obtain this file descriptor and arrange for the caller to | |
89 //! have access to it before calling RunServer(). | |
90 //! | |
91 //! \return The file descriptor that the client should read from. | |
92 int ReadPipeFD() const; | |
93 | |
94 //! \brief Runs the server. | |
95 //! | |
96 //! This method performs these tasks: | |
97 //! - Closes the “read” side of the pipe in-process, so that the client | |
98 //! process holds the only file descriptor that can read from the pipe. | |
99 //! - Creates a random token and sends it via the pipe. | |
100 //! - Checks its service in with the bootstrap server, and sends the name | |
101 //! of its bootstrap service mapping via the pipe. | |
102 //! - Simultaneously receives messages on its Mach server and monitors the | |
103 //! pipe for end-of-file. This is a blocking operation. | |
104 //! - When a Mach message is received, calls HandleChildPortCheckIn() to | |
105 //! interpret and validate it, and if the message is valid, returns the | |
106 //! port right extracted from the message. If the message is not valid, | |
107 //! this method will continue waiting for a valid message. Valid messages | |
108 //! are properly formatted and have the correct token. If a valid message | |
109 //! carries a send or send-once right, it will be returned. If a valid | |
110 //! message contains a receive right, it will be destroyed and | |
111 //! `MACH_PORT_NULL` will be returned. If a message is not valid, this | |
112 //! method will continue waiting for pipe EOF or a valid message. | |
113 //! - When notified of pipe EOF, returns `MACH_PORT_NULL`. | |
114 //! - Regardless of return value, destroys the server’s receive right and | |
115 //! closes the pipe. | |
116 //! | |
117 //! \return On success, the send or send-once right to the port provided by | |
118 //! the client. The caller takes ownership of this right. On failure, | |
119 //! `MACH_PORT_NULL`, indicating that the client did not check in properly | |
120 //! before terminating, where termination is detected by noticing that the | |
121 //! read side of the shared pipe has closed. On failure, a message | |
122 //! indiciating the nature of the failure will be logged. | |
123 mach_port_t RunServer(); | |
124 | |
125 // ChildPortServer::Interface: | |
126 kern_return_t HandleChildPortCheckIn(child_port_server_t server, | |
127 child_port_token_t token, | |
128 mach_port_t port, | |
129 mach_msg_type_name_t right_type, | |
130 bool* destroy_complex_request) override; | |
131 | |
132 //! \brief Runs the read-from-pipe portion of the client’s side of the | |
133 //! handshake. This is an implementation detail of RunClient and is only | |
Robert Sesek
2014/11/25 18:29:27
The alternative to this would be to move this into
| |
134 //! exposed for testing purposes. | |
135 //! | |
136 //! \param[in] pipe_read The “read” side of the pipe shared with the server | |
137 //! process. | |
138 //! \param[out] token The token value read from \a pipe_read. | |
139 //! \param[out] service_name The service name as registered with the bootstrap | |
140 //! server, read from \a pipe_read. | |
141 static void RunClientInternal_ReadPipe(int pipe_read, | |
142 child_port_token_t* token, | |
143 std::string* service_name); | |
144 | |
145 //! \brief Runs the check-in portion of the client’s side of the handshake. | |
146 //! This is an implementation detail of RunClient and is only exposed for | |
147 //! testing purposes. | |
148 //! | |
149 //! \param[in] service_name The service name as registered with the bootstrap | |
150 //! server, to be looked up with `bootstrap_look_up()`. | |
151 //! \param[in] token The token value to provide during check-in. | |
152 //! \param[in] port The port that will be passed to the server by | |
153 //! `child_port_check_in()`. | |
154 //! \param[in] right_type The right type to furnish the parent with. | |
155 static void RunClientInternal_SendCheckIn(const std::string& service_name, | |
156 child_port_token_t token, | |
157 mach_port_t port, | |
158 mach_msg_type_name_t right_type); | |
159 | |
160 //! \brief Runs the client. | |
Robert Sesek
2014/11/25 18:29:27
If this isn't internal, move this above the intern
| |
161 //! | |
162 //! This function performs these tasks: | |
163 //! - Reads the token from the pipe. | |
164 //! - Reads the bootstrap service name from the pipe. | |
165 //! - Obtains a send right to the server by calling `bootstrap_look_up()`. | |
166 //! - Sends a check-in message to the server by calling | |
167 //! `child_port_check_in()`, providing the token and the user-supplied port | |
168 //! right. | |
169 //! - Deallocates the send right to the server, and closes the pipe. | |
170 //! | |
171 //! \param[in] pipe_read The “read” side of the pipe shared with the server | |
172 //! process. | |
173 //! \param[in] port The port that will be passed to the server by | |
174 //! `child_port_check_in()`. | |
175 //! \param[in] right_type The right type to furnish the parent with. If \a | |
176 //! port is a send right, this can be `MACH_MSG_TYPE_COPY_SEND` or | |
177 //! `MACH_MSG_TYPE_MOVE_SEND`. If \a port is a send-once right, this can | |
178 //! be `MACH_MSG_TYPE_MOVE_SEND_ONCE`. If \a port is a receive right, | |
179 //! this can be `MACH_MSG_TYPE_MAKE_SEND`. `MACH_MSG_TYPE_MOVE_RECEIVE` | |
180 //! is supported by the client interface but will be silently rejected by | |
181 //! server run by RunServer(), which expects to receive only send or | |
182 //! send-once rights. | |
183 static void RunClient(int pipe_read, | |
Robert Sesek
2014/11/25 18:29:26
Should this return the status of the check-in?
| |
184 mach_port_t port, | |
185 mach_msg_type_name_t right_type); | |
186 | |
187 private: | |
188 // Communicates the token from RunServer(), where it’s generated, to | |
189 // HandleChildPortCheckIn(), where it’s validated. | |
190 child_port_token_t token_; | |
191 | |
192 base::ScopedFD pipe_read_; | |
193 base::ScopedFD pipe_write_; | |
194 | |
195 // Communicates the port received from the client from | |
196 // HandleChildPortCheckIn(), where it’s received, to RunServer(), where it’s | |
197 // returned. This is strongly-owned, but ownership is transferred to | |
198 // RunServer()’s caller. | |
199 mach_port_t child_port_; | |
200 | |
201 // Communicates that a check-in with a valid token was received by | |
202 // HandleChildPortCheckIn(), and that the value of child_port_ should be | |
203 // returned to RunServer()’s caller. | |
204 bool checked_in_; | |
205 | |
206 DISALLOW_COPY_AND_ASSIGN(ChildPortHandshake); | |
207 }; | |
208 | |
209 } // namespace crashpad | |
210 | |
211 #endif // CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_ | |
OLD | NEW |