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