]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2000-2004,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | // | |
26 | // mach++ - C++ bindings for useful Mach primitives | |
27 | // | |
79b9da22 A |
28 | #define __STDC_WANT_LIB_EXT1__ 1 |
29 | #include <string.h> | |
30 | ||
b1ab9ed8 A |
31 | #include <security_utilities/mach++.h> |
32 | #include <mach/mach_error.h> | |
33 | #include <security_utilities/debugging.h> | |
b1ab9ed8 A |
34 | #include <servers/bootstrap_defs.h> // debug |
35 | #include <bootstrap_priv.h> | |
36 | ||
37 | namespace Security { | |
38 | namespace MachPlusPlus { | |
39 | ||
40 | ||
41 | // | |
42 | // Mach subsystem exceptions, a subclass of CssmCommonError | |
43 | // | |
44 | Error::Error(kern_return_t err) : error(err) | |
45 | { | |
fa7225c8 A |
46 | SECURITY_EXCEPTION_THROW_MACH(this, err); |
47 | secnotice("security_exception", "mach error: %d", err); | |
b1ab9ed8 A |
48 | } |
49 | ||
d64be36e | 50 | Error::~Error() _NOEXCEPT |
b1ab9ed8 A |
51 | { } |
52 | ||
53 | ||
54 | OSStatus Error::osStatus() const | |
55 | { | |
56 | switch (error) { | |
57 | case MIG_BAD_ARGUMENTS: | |
58 | case MIG_TYPE_ERROR: | |
59 | case MIG_REMOTE_ERROR: | |
866f8763 | 60 | return errSecServiceNotAvailable; // IPC mismatch of some sort |
b1ab9ed8 A |
61 | default: |
62 | return -1; //@@@ some "internal error" code, perhaps? | |
63 | } | |
64 | } | |
65 | ||
66 | int Error::unixError() const | |
67 | { | |
68 | switch (error) { | |
69 | case MIG_BAD_ARGUMENTS: | |
70 | case MIG_TYPE_ERROR: | |
71 | case MIG_REMOTE_ERROR: | |
72 | return ERPCMISMATCH; // IPC mismatch of some sort | |
73 | default: | |
74 | return -1; //@@@ some "internal error" code, perhaps? | |
75 | } | |
76 | } | |
77 | ||
78 | void Error::check(kern_return_t status) | |
79 | { | |
80 | if (status != KERN_SUCCESS) | |
81 | Error::throwMe(status); | |
82 | } | |
83 | ||
84 | void Error::throwMe(kern_return_t err) | |
85 | { | |
86 | throw Error(err); | |
87 | } | |
88 | ||
89 | ||
90 | // | |
91 | // Memory management | |
92 | // | |
93 | void *allocate(size_t size) | |
94 | { | |
95 | vm_address_t address; | |
96 | check(vm_allocate(mach_task_self(), &address, size, true)); | |
97 | return reinterpret_cast<void *>(address); | |
98 | } | |
99 | ||
100 | void deallocate(vm_address_t address, size_t size) | |
101 | { | |
102 | check(vm_deallocate(mach_task_self(), address, size)); | |
103 | } | |
104 | ||
105 | ||
106 | // | |
107 | // Port functions | |
108 | // | |
109 | mach_port_urefs_t Port::getRefs(mach_port_right_t right) | |
110 | { | |
111 | mach_port_urefs_t count; | |
112 | check(::mach_port_get_refs(self(), mPort, right, &count)); | |
113 | return count; | |
114 | } | |
115 | ||
116 | mach_port_t Port::requestNotify(mach_port_t notify, mach_msg_id_t type, mach_port_mscount_t sync) | |
117 | { | |
118 | mach_port_t previous; | |
119 | check(mach_port_request_notification(self(), mPort, type, sync, notify, | |
120 | MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous)); | |
121 | ||
122 | #if !defined(NDEBUG) | |
123 | const char *typeName; | |
124 | switch (type) { | |
125 | case MACH_NOTIFY_PORT_DELETED: typeName = "port deleted"; break; | |
126 | case MACH_NOTIFY_PORT_DESTROYED:typeName = "port destroyed"; break; | |
127 | case MACH_NOTIFY_NO_SENDERS: typeName = "no senders"; break; | |
128 | case MACH_NOTIFY_SEND_ONCE: typeName = "send once"; break; | |
129 | case MACH_NOTIFY_DEAD_NAME: typeName = "dead name"; break; | |
130 | default: typeName = "???"; break; | |
131 | } | |
132 | if (notify == MACH_PORT_NULL) | |
fa7225c8 | 133 | secinfo("port", "%d cancel notify %s", port(), typeName); |
b1ab9ed8 | 134 | else |
fa7225c8 | 135 | secinfo("port", "%d request notify %s to %d (sync %d)", port(), typeName, notify, sync); |
b1ab9ed8 A |
136 | #endif //!NDEBUG |
137 | ||
138 | return previous; | |
139 | } | |
140 | ||
141 | mach_port_t Port::cancelNotify(mach_msg_id_t type) | |
142 | { | |
143 | // Mach won't let us unset the DPN port if we are already dead | |
144 | // (EVEN if the DPN has already been sent!) So just ignore that case... | |
d64be36e | 145 | if (isDead()) { |
b1ab9ed8 | 146 | return MACH_PORT_NULL; |
d64be36e A |
147 | } |
148 | return requestNotify(MACH_PORT_NULL, type); | |
b1ab9ed8 A |
149 | } |
150 | ||
151 | mach_port_msgcount_t Port::qlimit() const | |
152 | { | |
153 | mach_port_limits_t limits; | |
154 | mach_msg_type_number_t infoCount = 1; | |
155 | check(::mach_port_get_attributes(self(), mPort, MACH_PORT_LIMITS_INFO, | |
156 | mach_port_info_t(&limits), &infoCount)); | |
157 | assert(infoCount == 1); | |
158 | return limits.mpl_qlimit; | |
159 | } | |
160 | ||
161 | void Port::qlimit(mach_port_msgcount_t limit) | |
162 | { | |
163 | mach_port_limits_t limits; | |
164 | limits.mpl_qlimit = limit; | |
165 | check(::mach_port_set_attributes(self(), mPort, MACH_PORT_LIMITS_INFO, | |
166 | mach_port_info_t(&limits), MACH_PORT_LIMITS_INFO_COUNT)); | |
167 | } | |
168 | ||
169 | ||
170 | // | |
171 | // PortSet features | |
172 | // | |
173 | set<Port> PortSet::members() const | |
174 | { | |
175 | mach_port_array_t members; | |
176 | mach_msg_type_number_t count; | |
177 | check(::mach_port_get_set_status(self(), mPort, &members, &count)); | |
178 | try { | |
179 | set<Port> result; | |
180 | copy(members, members+count, inserter(result, result.begin())); | |
181 | vm_deallocate(self(), vm_address_t(members), count * sizeof(members[0])); | |
182 | return result; | |
183 | } catch (...) { | |
184 | vm_deallocate(self(), vm_address_t(members), count * sizeof(members[0])); | |
185 | throw; | |
186 | } | |
187 | } | |
188 | ||
189 | ||
190 | bool PortSet::contains(Port member) const | |
191 | { | |
192 | set<Port> memberSet = members(); | |
193 | return memberSet.find(member) != memberSet.end(); | |
194 | } | |
195 | ||
196 | ||
197 | // | |
198 | // Task port features | |
199 | // | |
200 | TaskPort::TaskPort(pid_t pid) | |
201 | { | |
202 | check(::task_for_pid(self(), pid, &mPort)); | |
203 | } | |
204 | ||
205 | pid_t TaskPort::pid() const | |
206 | { | |
207 | pid_t pid; | |
208 | check(::pid_for_task(mPort, &pid)); | |
209 | return pid; | |
210 | } | |
211 | ||
212 | ||
213 | // | |
214 | // Bootstrap port management | |
215 | // | |
216 | mach_port_t Bootstrap::checkIn(const char *name) const | |
217 | { | |
218 | mach_port_t port; | |
219 | check(::bootstrap_check_in(mPort, makeName(name), &port)); | |
220 | return port; | |
221 | } | |
222 | ||
223 | mach_port_t Bootstrap::checkInOptional(const char *name) const | |
224 | { | |
225 | mach_port_t port; | |
226 | switch (kern_return_t err = ::bootstrap_check_in(mPort, makeName(name), &port)) { | |
227 | case BOOTSTRAP_SERVICE_ACTIVE: | |
228 | case BOOTSTRAP_UNKNOWN_SERVICE: | |
229 | case BOOTSTRAP_NOT_PRIVILEGED: | |
230 | return MACH_PORT_NULL; | |
231 | default: | |
232 | check(err); | |
233 | } | |
234 | return port; | |
235 | } | |
236 | ||
237 | void Bootstrap::registerAs(mach_port_t port, const char *name) const | |
238 | { | |
fa7225c8 | 239 | secinfo("bootstrap", "creating service port %d in %d:%s", port, this->port(), name); |
b1ab9ed8 A |
240 | check(::bootstrap_register(mPort, makeName(name), port)); |
241 | } | |
242 | ||
243 | mach_port_t Bootstrap::lookup(const char *name) const | |
244 | { | |
245 | mach_port_t port; | |
246 | check(::bootstrap_look_up(mPort, makeName(name), &port)); | |
247 | return port; | |
248 | } | |
249 | ||
250 | mach_port_t Bootstrap::lookup2(const char *name) const | |
251 | { | |
252 | mach_port_t port; | |
253 | check(::bootstrap_look_up2(mPort, makeName(name), &port, 0, BOOTSTRAP_PRIVILEGED_SERVER)); | |
254 | return port; | |
255 | } | |
256 | ||
257 | mach_port_t Bootstrap::lookupOptional(const char *name) const | |
258 | { | |
259 | mach_port_t port; | |
260 | kern_return_t err = ::bootstrap_look_up(mPort, makeName(name), &port); | |
261 | if (err == BOOTSTRAP_UNKNOWN_SERVICE) | |
262 | return 0; | |
263 | check(err); | |
264 | return port; | |
265 | } | |
266 | ||
267 | ||
268 | Bootstrap Bootstrap::subset(Port requestor) | |
269 | { | |
270 | mach_port_t sub; | |
271 | check(::bootstrap_subset(mPort, requestor, &sub)); | |
272 | return sub; | |
273 | } | |
274 | ||
275 | ||
276 | // | |
277 | // ReceivePorts | |
278 | // | |
279 | ReceivePort::ReceivePort(const char *name, const Bootstrap &bootstrap, bool tryCheckin /* = true */) | |
280 | { | |
281 | if (tryCheckin) | |
282 | mPort = bootstrap.checkInOptional(name); | |
283 | if (!mPort) { | |
284 | allocate(); | |
285 | // Bootstrap registration requires a send right to (copy) send. | |
286 | // Make a temporary one, send it, then take it away again, to avoid | |
287 | // messing up the caller's send right accounting. | |
288 | insertRight(MACH_MSG_TYPE_MAKE_SEND); | |
289 | bootstrap.registerAs(mPort, name); | |
290 | modRefs(MACH_PORT_RIGHT_SEND, -1); | |
291 | } | |
292 | } | |
293 | ||
294 | ||
295 | // | |
296 | // Stack-based bootstrap switcher | |
297 | // | |
298 | ModuleNexus<Mutex> StBootstrap::critical; | |
299 | ||
300 | StBootstrap::StBootstrap(const Bootstrap &newBoot, const TaskPort &task) | |
301 | : mTask(task), locker(critical()) | |
302 | { | |
303 | mOldBoot = Bootstrap(); | |
304 | mTask.bootstrap(newBoot); | |
fa7225c8 | 305 | secinfo("StBoot", "bootstrap for %d switched to %d", mTask.port(), newBoot.port()); |
b1ab9ed8 A |
306 | } |
307 | ||
308 | StBootstrap::~StBootstrap() | |
309 | { | |
310 | mTask.bootstrap(mOldBoot); | |
fa7225c8 | 311 | secinfo("StBoot", "bootstrap for %d returned to %d", mTask.port(), mOldBoot.port()); |
b1ab9ed8 A |
312 | } |
313 | ||
314 | ||
315 | // | |
316 | // Mach message buffers | |
317 | // | |
427c49bc | 318 | Message::Message(void *buffer, mach_msg_size_t size) |
b1ab9ed8 A |
319 | : mBuffer(NULL), mRelease(false) |
320 | { | |
321 | setBuffer(buffer, size); | |
322 | } | |
323 | ||
427c49bc | 324 | Message::Message(mach_msg_size_t size) |
b1ab9ed8 A |
325 | : mBuffer(NULL), mRelease(false) |
326 | { | |
327 | setBuffer(size); | |
328 | } | |
329 | ||
330 | Message::Message() | |
331 | : mBuffer(NULL), mRelease(false) | |
332 | { } | |
333 | ||
334 | ||
335 | Message::~Message() | |
336 | { | |
337 | release(); | |
338 | } | |
339 | ||
340 | ||
427c49bc | 341 | void Message::setBuffer(void *buffer, mach_msg_size_t size) |
b1ab9ed8 A |
342 | { |
343 | release(); | |
344 | mBuffer = reinterpret_cast<mig_reply_error_t *>(buffer); | |
345 | mSize = size; | |
346 | mRelease = false; | |
347 | } | |
348 | ||
427c49bc | 349 | void Message::setBuffer(mach_msg_size_t size) |
b1ab9ed8 A |
350 | { |
351 | assert(size >= sizeof(mach_msg_header_t)); | |
352 | release(); | |
353 | mSize = size + MAX_TRAILER_SIZE; | |
79b9da22 | 354 | mBuffer = reinterpret_cast<mig_reply_error_t *>(new char[mSize]()); |
b1ab9ed8 A |
355 | mRelease = true; |
356 | } | |
357 | ||
79b9da22 A |
358 | void Message::clearBuffer(void) |
359 | { | |
360 | (void)memset_s(mBuffer, mSize, 0, mSize); | |
361 | } | |
b1ab9ed8 A |
362 | |
363 | void Message::release() | |
364 | { | |
365 | if (mRelease) | |
366 | delete[] reinterpret_cast<char *>(mBuffer); | |
367 | } | |
368 | ||
369 | ||
370 | bool Message::check(kern_return_t status) | |
371 | { | |
372 | switch (status) { | |
373 | case KERN_SUCCESS: | |
374 | return true; | |
375 | case MACH_RCV_TIMED_OUT: | |
376 | case MACH_SEND_TIMED_OUT: | |
377 | return false; | |
378 | default: | |
379 | Error::throwMe(status); | |
380 | } | |
381 | } | |
382 | ||
383 | ||
384 | bool Message::send(mach_msg_option_t options, | |
385 | mach_msg_timeout_t timeout, | |
386 | mach_port_name_t notify) | |
387 | { | |
388 | return check(mach_msg_overwrite(*this, | |
389 | options | MACH_SEND_MSG, | |
390 | length(), | |
391 | 0, MACH_PORT_NULL, | |
392 | timeout, notify, | |
393 | NULL, 0)); | |
394 | } | |
395 | ||
396 | bool Message::receive(mach_port_t receivePort, | |
397 | mach_msg_option_t options, | |
398 | mach_msg_timeout_t timeout, | |
399 | mach_port_name_t notify) | |
400 | { | |
401 | return check(mach_msg_overwrite(*this, | |
402 | options | MACH_RCV_MSG, | |
403 | length(), | |
404 | mSize, receivePort, | |
405 | timeout, notify, | |
406 | NULL, 0)); | |
407 | } | |
408 | ||
409 | bool Message::sendReceive(mach_port_t receivePort, | |
410 | mach_msg_option_t options, | |
411 | mach_msg_timeout_t timeout, | |
412 | mach_port_name_t notify) | |
413 | { | |
414 | return check(mach_msg_overwrite(*this, | |
415 | options | MACH_SEND_MSG | MACH_RCV_MSG, | |
416 | length(), | |
417 | mSize, receivePort, | |
418 | timeout, notify, | |
419 | NULL, 0)); | |
420 | } | |
421 | ||
0e1db9d1 A |
422 | /* cribbed from libdispatch: _dispatch_mach_msg_get_audit_trailer */ |
423 | mach_msg_audit_trailer_t * | |
424 | Message::auditTrailer() | |
425 | { | |
426 | mach_msg_header_t *hdr = &mBuffer->Head; | |
427 | mach_msg_trailer_t *tlr = NULL; | |
428 | mach_msg_audit_trailer_t *audit_tlr = NULL; | |
429 | tlr = (mach_msg_trailer_t *)((unsigned char *)hdr + | |
430 | round_msg(hdr->msgh_size)); | |
431 | // The trailer should always be of format zero. | |
432 | if (tlr->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0) { | |
433 | if (tlr->msgh_trailer_size >= sizeof(mach_msg_audit_trailer_t)) { | |
434 | audit_tlr = (mach_msg_audit_trailer_t *)tlr; | |
435 | } | |
436 | } | |
437 | return audit_tlr; | |
438 | } | |
b1ab9ed8 A |
439 | |
440 | // | |
441 | // Debug dumping of ports etc. | |
442 | // | |
443 | #if defined(DEBUGDUMP) | |
444 | ||
445 | void Port::dump(const char *descr) | |
446 | { | |
447 | if (mPort == MACH_PORT_NULL) { | |
448 | Debug::dump("[%s==NULL]\n", descr ? descr : "port"); | |
449 | } else { | |
450 | Debug::dump("[%s(%d)", descr ? descr : "port", mPort); | |
451 | mach_port_type_t type; | |
452 | if (kern_return_t err = mach_port_type(self(), mPort, &type)) { | |
453 | Debug::dump(" !%s", mach_error_string(err)); | |
454 | } else { | |
455 | if (type & MACH_PORT_TYPE_SEND) | |
456 | Debug::dump(" send(%d)", getRefs(MACH_PORT_RIGHT_SEND)); | |
457 | if (type & MACH_PORT_TYPE_RECEIVE) | |
458 | Debug::dump(" rcv"); | |
459 | if (type & MACH_PORT_TYPE_SEND_ONCE) | |
460 | Debug::dump(" once(%d)", getRefs(MACH_PORT_RIGHT_SEND)); | |
461 | if (type & MACH_PORT_TYPE_PORT_SET) | |
462 | Debug::dump(" set"); | |
463 | if (type & MACH_PORT_TYPE_DEAD_NAME) | |
464 | Debug::dump(" dead(%d)", getRefs(MACH_PORT_RIGHT_SEND)); | |
465 | if (type & MACH_PORT_TYPE_DNREQUEST) | |
466 | Debug::dump(" dnreq"); | |
467 | // handle unknown/unexpected type flags | |
468 | if (type & ~(MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_SEND_ONCE| | |
469 | MACH_PORT_TYPE_PORT_SET|MACH_PORT_TYPE_DEAD_NAME|MACH_PORT_TYPE_DNREQUEST)) | |
470 | Debug::dump(" type(0x%x)", type); | |
471 | } | |
472 | Debug::dump("]\n"); | |
473 | } | |
474 | } | |
475 | ||
476 | ||
477 | #endif //DEBUGDUMP | |
478 | ||
479 | ||
480 | } // end namespace MachPlusPlus | |
481 | } // end namespace Security |