Chromium Code Reviews| 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 |