]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_utilities/lib/mach++.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / mach++.cpp
CommitLineData
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
37namespace Security {
38namespace MachPlusPlus {
39
40
41//
42// Mach subsystem exceptions, a subclass of CssmCommonError
43//
44Error::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 50Error::~Error() _NOEXCEPT
b1ab9ed8
A
51{ }
52
53
54OSStatus 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
66int 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
78void Error::check(kern_return_t status)
79{
80 if (status != KERN_SUCCESS)
81 Error::throwMe(status);
82}
83
84void Error::throwMe(kern_return_t err)
85{
86 throw Error(err);
87}
88
89
90//
91// Memory management
92//
93void *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
100void 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//
109mach_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
116mach_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
141mach_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
151mach_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
161void 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//
173set<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
190bool 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//
200TaskPort::TaskPort(pid_t pid)
201{
202 check(::task_for_pid(self(), pid, &mPort));
203}
204
205pid_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//
216mach_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
223mach_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
237void 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
243mach_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
250mach_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
257mach_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
268Bootstrap 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//
279ReceivePort::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//
298ModuleNexus<Mutex> StBootstrap::critical;
299
300StBootstrap::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
308StBootstrap::~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 318Message::Message(void *buffer, mach_msg_size_t size)
b1ab9ed8
A
319 : mBuffer(NULL), mRelease(false)
320{
321 setBuffer(buffer, size);
322}
323
427c49bc 324Message::Message(mach_msg_size_t size)
b1ab9ed8
A
325 : mBuffer(NULL), mRelease(false)
326{
327 setBuffer(size);
328}
329
330Message::Message()
331 : mBuffer(NULL), mRelease(false)
332{ }
333
334
335Message::~Message()
336{
337 release();
338}
339
340
427c49bc 341void 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 349void 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
358void Message::clearBuffer(void)
359{
360 (void)memset_s(mBuffer, mSize, 0, mSize);
361}
b1ab9ed8
A
362
363void Message::release()
364{
365 if (mRelease)
366 delete[] reinterpret_cast<char *>(mBuffer);
367}
368
369
370bool 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
384bool 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
396bool 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
409bool 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 */
423mach_msg_audit_trailer_t *
424Message::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
445void 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