]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/mach++.cpp
Security-54.tar.gz
[apple/security.git] / cdsa / cdsa_utilities / mach++.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // mach++ - C++ bindings for useful Mach primitives
21 //
22 #include "mach++.h"
23 #include <mach/mach_error.h>
24 #include <stdio.h> // debug
25 #include <Security/debugging.h>
26 #include <servers/bootstrap_defs.h> // debug
27
28 namespace Security {
29 namespace MachPlusPlus {
30
31
32 Error::Error(kern_return_t err) : error(err)
33 {
34 }
35
36 Error::~Error() throw()
37 {
38 }
39
40 CSSM_RETURN
41 Error::cssmError() const
42 {
43 switch (error) {
44 case BOOTSTRAP_UNKNOWN_SERVICE:
45 case MIG_SERVER_DIED:
46 return CSSM_ERRCODE_SERVICE_NOT_AVAILABLE;
47 default:
48 return CSSM_ERRCODE_INTERNAL_ERROR;
49 }
50 }
51
52 OSStatus
53 Error::osStatus() const
54 {
55 return cssmError();
56 }
57
58 void Error::check(kern_return_t status)
59 {
60 if (status != KERN_SUCCESS)
61 Error::throwMe(status);
62 }
63
64 void Error::throwMe(kern_return_t err)
65 {
66 throw Error(err);
67 }
68
69
70 #if !defined(NDEBUG)
71 void Error::debugDiagnose(const void *id) const
72 {
73 const char *name;
74 switch (error) {
75 default:
76 name = mach_error_string(error); break;
77 case BOOTSTRAP_UNKNOWN_SERVICE:
78 name = "BOOTSTRAP_UNKNOWN_SERVICE"; break;
79 case BOOTSTRAP_NAME_IN_USE:
80 name = "BOOTSTRAP_NAME_IN_USE"; break;
81 case BOOTSTRAP_NOT_PRIVILEGED:
82 name = "BOOTSTRAP_NOT_PRIVILEGED"; break;
83 case BOOTSTRAP_SERVICE_ACTIVE:
84 name = "BOOTSTRAP_SERVICE_ACTIVE"; break;
85 }
86 debug("exception", "%p Mach Error %s (%d) osStatus %ld",
87 id, name, error, osStatus());
88 }
89 #endif //NDEBUG
90
91
92 //
93 // Memory management
94 //
95 void *allocate(size_t size)
96 {
97 vm_address_t address;
98 check(vm_allocate(mach_task_self(), &address, size, true));
99 return reinterpret_cast<void *>(address);
100 }
101
102 void deallocate(vm_address_t address, size_t size)
103 {
104 check(vm_deallocate(mach_task_self(), address, size));
105 }
106
107
108 //
109 // Port functions
110 //
111 mach_port_urefs_t Port::getRefs(mach_port_right_t right)
112 {
113 mach_port_urefs_t count;
114 check(::mach_port_get_refs(self(), mPort, right, &count));
115 return count;
116 }
117 mach_port_t Port::requestNotify(mach_port_t notify, mach_msg_id_t type, mach_port_mscount_t sync)
118 {
119 mach_port_t previous;
120 check(mach_port_request_notification(self(), mPort, type, sync, notify,
121 MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous));
122 debug("port", "%d request notify(%d) to %d (sync=%d)", port(), type, notify, sync);
123 return previous;
124 }
125
126 mach_port_t Port::cancelNotify(mach_msg_id_t type)
127 {
128 // Mach won't let us unset the DPN port if we are already dead
129 // (EVEN if the DPN has already been sent!) So just ignore that case...
130 if (isDead())
131 return MACH_PORT_NULL;
132 debug("port", "%d cancel DPN", port());
133 return requestNotify(MACH_PORT_NULL, type);
134 }
135
136
137 //
138 // PortSet features
139 //
140 set<Port> PortSet::members() const
141 {
142 mach_port_array_t members;
143 mach_msg_type_number_t count;
144 check(::mach_port_get_set_status(self(), mPort, &members, &count));
145 try {
146 set<Port> result;
147 copy(members, members+count, inserter(result, result.begin()));
148 vm_deallocate(self(), vm_address_t(members), count * sizeof(members[0]));
149 return result;
150 } catch (...) {
151 vm_deallocate(self(), vm_address_t(members), count * sizeof(members[0]));
152 throw;
153 }
154 }
155
156
157 bool PortSet::contains(Port member) const
158 {
159 set<Port> memberSet = members();
160 return memberSet.find(member) != memberSet.end();
161 }
162
163
164 //
165 // Task port features
166 //
167 pid_t TaskPort::pid() const
168 {
169 pid_t pid;
170 check(::pid_for_task(mPort, &pid));
171 return pid;
172 }
173
174 TaskPort TaskPort::forPid(pid_t pid)
175 {
176 TaskPort taskPort;
177 check(::task_for_pid(self(), pid, &taskPort.port()));
178 return taskPort;
179 }
180
181
182 //
183 // Bootstrap port management
184 //
185 mach_port_t Bootstrap::checkIn(const char *name) const
186 {
187 mach_port_t port;
188 check(::bootstrap_check_in(mPort, makeName(name), &port));
189 return port;
190 }
191
192 mach_port_t Bootstrap::checkInOptional(const char *name) const
193 {
194 mach_port_t port;
195 switch (kern_return_t err = ::bootstrap_check_in(mPort, makeName(name), &port)) {
196 case BOOTSTRAP_SERVICE_ACTIVE:
197 case BOOTSTRAP_UNKNOWN_SERVICE:
198 case BOOTSTRAP_NOT_PRIVILEGED:
199 return 0;
200 default:
201 check(err);
202 }
203 return port;
204 }
205
206 void Bootstrap::registerAs(mach_port_t port, const char *name) const
207 {
208 debug("bootstrap", "creating service port %d in %d:%s", port, this->port(), name);
209 check(::bootstrap_register(mPort, makeName(name), port));
210 }
211
212 mach_port_t Bootstrap::lookup(const char *name) const
213 {
214 mach_port_t port;
215 check(::bootstrap_look_up(mPort, makeName(name), &port));
216 return port;
217 }
218
219 mach_port_t Bootstrap::lookupOptional(const char *name) const
220 {
221 mach_port_t port;
222 kern_return_t err = ::bootstrap_look_up(mPort, makeName(name), &port);
223 if (err == BOOTSTRAP_UNKNOWN_SERVICE)
224 return 0;
225 check(err);
226 return port;
227 }
228
229
230 Bootstrap Bootstrap::subset(Port requestor)
231 {
232 mach_port_t sub;
233 check(::bootstrap_subset(mPort, requestor, &sub));
234 return sub;
235 }
236
237
238 //
239 // ReceivePorts
240 //
241 ReceivePort::ReceivePort(const char *name, const Bootstrap &bootstrap)
242 {
243 mPort = bootstrap.checkInOptional(name);
244 if (!mPort) {
245 allocate();
246 // Bootstrap registration requires a send right to (copy) send.
247 // Make a temporary one, send it, then take it away again, to avoid
248 // messing up the caller's send right accounting.
249 insertRight(MACH_MSG_TYPE_MAKE_SEND);
250 bootstrap.registerAs(mPort, name);
251 modRefs(MACH_PORT_RIGHT_SEND, -1);
252 }
253 }
254
255
256 //
257 // Stack-based bootstrap switcher
258 //
259 ModuleNexus<Mutex> StBootstrap::critical;
260
261 StBootstrap::StBootstrap(const Bootstrap &newBoot, const TaskPort &task)
262 : mTask(task), locker(critical())
263 {
264 mOldBoot = Bootstrap();
265 mTask.bootstrap(newBoot);
266 debug("StBoot", "bootstrap for %d switched to %d", mTask.port(), newBoot.port());
267 }
268
269 StBootstrap::~StBootstrap()
270 {
271 mTask.bootstrap(mOldBoot);
272 debug("StBoot", "bootstrap for %d returned to %d", mTask.port(), mOldBoot.port());
273 }
274
275
276 //
277 // Mach message buffers
278 //
279 Message::Message(void *buffer, size_t size)
280 : mBuffer(reinterpret_cast<mig_reply_error_t *>(buffer)),
281 mSize(size), mRelease(false)
282 {
283 assert(size >= sizeof(mach_msg_header_t));
284 }
285
286 Message::Message(size_t size)
287 {
288 mSize = size + MAX_TRAILER_SIZE;
289 mBuffer = (mig_reply_error_t *)new char[mSize];
290 mRelease = true;
291 }
292
293 Message::~Message()
294 {
295 if (mRelease)
296 delete[] mBuffer;
297 }
298
299
300 bool Message::check(kern_return_t status)
301 {
302 switch (status) {
303 case KERN_SUCCESS:
304 return true;
305 case MACH_RCV_TIMED_OUT:
306 case MACH_SEND_TIMED_OUT:
307 return false;
308 default:
309 Error::throwMe(status);
310 }
311 }
312
313
314 bool Message::send(mach_msg_option_t options,
315 mach_msg_timeout_t timeout,
316 mach_port_name_t notify)
317 {
318 return check(mach_msg_overwrite(*this,
319 options | MACH_SEND_MSG,
320 length(),
321 0, MACH_PORT_NULL,
322 timeout, notify,
323 NULL, 0));
324 }
325
326 bool Message::receive(mach_port_t receivePort,
327 mach_msg_option_t options,
328 mach_msg_timeout_t timeout,
329 mach_port_name_t notify)
330 {
331 return check(mach_msg_overwrite(*this,
332 options | MACH_RCV_MSG,
333 length(),
334 mSize, receivePort,
335 timeout, notify,
336 NULL, 0));
337 }
338
339 bool Message::sendReceive(mach_port_t receivePort,
340 mach_msg_option_t options,
341 mach_msg_timeout_t timeout,
342 mach_port_name_t notify)
343 {
344 return check(mach_msg_overwrite(*this,
345 options | MACH_SEND_MSG | MACH_RCV_MSG,
346 length(),
347 mSize, receivePort,
348 timeout, notify,
349 NULL, 0));
350 }
351
352
353 //
354 // Debug dumping of ports etc.
355 //
356 #if defined(DEBUGDUMP)
357
358 void Port::dump(const char *descr)
359 {
360 if (mPort == MACH_PORT_NULL) {
361 Debug::dump("[%s==NULL]\n", descr ? descr : "port");
362 } else {
363 Debug::dump("[%s(%d)", descr ? descr : "port", mPort);
364 mach_port_type_t type;
365 if (kern_return_t err = mach_port_type(self(), mPort, &type)) {
366 Debug::dump(" !%s", mach_error_string(err));
367 } else {
368 if (type & MACH_PORT_TYPE_SEND)
369 Debug::dump(" send(%d)", getRefs(MACH_PORT_RIGHT_SEND));
370 if (type & MACH_PORT_TYPE_RECEIVE)
371 Debug::dump(" rcv");
372 if (type & MACH_PORT_TYPE_SEND_ONCE)
373 Debug::dump(" once(%d)", getRefs(MACH_PORT_RIGHT_SEND));
374 if (type & MACH_PORT_TYPE_PORT_SET)
375 Debug::dump(" set");
376 if (type & MACH_PORT_TYPE_DEAD_NAME)
377 Debug::dump(" dead(%d)", getRefs(MACH_PORT_RIGHT_SEND));
378 if (type & MACH_PORT_TYPE_DNREQUEST)
379 Debug::dump(" dnreq");
380 // handle unknown/unexpected type flags
381 if (type & ~(MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_SEND_ONCE|
382 MACH_PORT_TYPE_PORT_SET|MACH_PORT_TYPE_DEAD_NAME|MACH_PORT_TYPE_DNREQUEST))
383 Debug::dump(" type(0x%x)", type);
384 }
385 Debug::dump("]\n");
386 }
387 }
388
389
390 void Bootstrap::dump()
391 {
392 name_array_t services, servers;
393 bool_array_t active;
394 mach_msg_type_number_t nServices, nServers, nActive;
395 check(bootstrap_info(mPort, &services, &nServices,
396 &servers, &nServers, &active, &nActive));
397 Port::dump();
398 Debug::dump(" %d services\n", nServices);
399 for (mach_msg_type_number_t n = 0; n < nServices; n++)
400 Debug::dump("%s\n", services[n]);
401 }
402
403 #endif //DEBUGDUMP
404
405
406 } // end namespace MachPlusPlus
407 } // end namespace Security