2 * Copyright (c) 2000-2004,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // mach++ - C++ bindings for useful Mach primitives
28 #define __STDC_WANT_LIB_EXT1__ 1
31 #include <security_utilities/mach++.h>
32 #include <mach/mach_error.h>
33 #include <security_utilities/debugging.h>
34 #include <servers/bootstrap_defs.h> // debug
35 #include <bootstrap_priv.h>
38 namespace MachPlusPlus
{
42 // Mach subsystem exceptions, a subclass of CssmCommonError
44 Error::Error(kern_return_t err
) : error(err
)
46 SECURITY_EXCEPTION_THROW_MACH(this, err
);
47 secnotice("security_exception", "mach error: %d", err
);
50 Error::~Error() _NOEXCEPT
54 OSStatus
Error::osStatus() const
57 case MIG_BAD_ARGUMENTS
:
59 case MIG_REMOTE_ERROR
:
60 return errSecServiceNotAvailable
; // IPC mismatch of some sort
62 return -1; //@@@ some "internal error" code, perhaps?
66 int Error::unixError() const
69 case MIG_BAD_ARGUMENTS
:
71 case MIG_REMOTE_ERROR
:
72 return ERPCMISMATCH
; // IPC mismatch of some sort
74 return -1; //@@@ some "internal error" code, perhaps?
78 void Error::check(kern_return_t status
)
80 if (status
!= KERN_SUCCESS
)
81 Error::throwMe(status
);
84 void Error::throwMe(kern_return_t err
)
93 void *allocate(size_t size
)
96 check(vm_allocate(mach_task_self(), &address
, size
, true));
97 return reinterpret_cast<void *>(address
);
100 void deallocate(vm_address_t address
, size_t size
)
102 check(vm_deallocate(mach_task_self(), address
, size
));
109 mach_port_urefs_t
Port::getRefs(mach_port_right_t right
)
111 mach_port_urefs_t count
;
112 check(::mach_port_get_refs(self(), mPort
, right
, &count
));
116 mach_port_t
Port::requestNotify(mach_port_t notify
, mach_msg_id_t type
, mach_port_mscount_t sync
)
118 mach_port_t previous
;
119 check(mach_port_request_notification(self(), mPort
, type
, sync
, notify
,
120 MACH_MSG_TYPE_MAKE_SEND_ONCE
, &previous
));
123 const char *typeName
;
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;
132 if (notify
== MACH_PORT_NULL
)
133 secinfo("port", "%d cancel notify %s", port(), typeName
);
135 secinfo("port", "%d request notify %s to %d (sync %d)", port(), typeName
, notify
, sync
);
141 mach_port_t
Port::cancelNotify(mach_msg_id_t type
)
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...
146 return MACH_PORT_NULL
;
148 return requestNotify(MACH_PORT_NULL
, type
);
151 mach_port_msgcount_t
Port::qlimit() const
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
;
161 void Port::qlimit(mach_port_msgcount_t limit
)
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
));
173 set
<Port
> PortSet::members() const
175 mach_port_array_t members
;
176 mach_msg_type_number_t count
;
177 check(::mach_port_get_set_status(self(), mPort
, &members
, &count
));
180 copy(members
, members
+count
, inserter(result
, result
.begin()));
181 vm_deallocate(self(), vm_address_t(members
), count
* sizeof(members
[0]));
184 vm_deallocate(self(), vm_address_t(members
), count
* sizeof(members
[0]));
190 bool PortSet::contains(Port member
) const
192 set
<Port
> memberSet
= members();
193 return memberSet
.find(member
) != memberSet
.end();
198 // Task port features
200 TaskPort::TaskPort(pid_t pid
)
202 check(::task_for_pid(self(), pid
, &mPort
));
205 pid_t
TaskPort::pid() const
208 check(::pid_for_task(mPort
, &pid
));
214 // Bootstrap port management
216 mach_port_t
Bootstrap::checkIn(const char *name
) const
219 check(::bootstrap_check_in(mPort
, makeName(name
), &port
));
223 mach_port_t
Bootstrap::checkInOptional(const char *name
) const
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
;
237 void Bootstrap::registerAs(mach_port_t port
, const char *name
) const
239 secinfo("bootstrap", "creating service port %d in %d:%s", port
, this->port(), name
);
240 check(::bootstrap_register(mPort
, makeName(name
), port
));
243 mach_port_t
Bootstrap::lookup(const char *name
) const
246 check(::bootstrap_look_up(mPort
, makeName(name
), &port
));
250 mach_port_t
Bootstrap::lookup2(const char *name
) const
253 check(::bootstrap_look_up2(mPort
, makeName(name
), &port
, 0, BOOTSTRAP_PRIVILEGED_SERVER
));
257 mach_port_t
Bootstrap::lookupOptional(const char *name
) const
260 kern_return_t err
= ::bootstrap_look_up(mPort
, makeName(name
), &port
);
261 if (err
== BOOTSTRAP_UNKNOWN_SERVICE
)
268 Bootstrap
Bootstrap::subset(Port requestor
)
271 check(::bootstrap_subset(mPort
, requestor
, &sub
));
279 ReceivePort::ReceivePort(const char *name
, const Bootstrap
&bootstrap
, bool tryCheckin
/* = true */)
282 mPort
= bootstrap
.checkInOptional(name
);
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);
296 // Stack-based bootstrap switcher
298 ModuleNexus
<Mutex
> StBootstrap::critical
;
300 StBootstrap::StBootstrap(const Bootstrap
&newBoot
, const TaskPort
&task
)
301 : mTask(task
), locker(critical())
303 mOldBoot
= Bootstrap();
304 mTask
.bootstrap(newBoot
);
305 secinfo("StBoot", "bootstrap for %d switched to %d", mTask
.port(), newBoot
.port());
308 StBootstrap::~StBootstrap()
310 mTask
.bootstrap(mOldBoot
);
311 secinfo("StBoot", "bootstrap for %d returned to %d", mTask
.port(), mOldBoot
.port());
316 // Mach message buffers
318 Message::Message(void *buffer
, mach_msg_size_t size
)
319 : mBuffer(NULL
), mRelease(false)
321 setBuffer(buffer
, size
);
324 Message::Message(mach_msg_size_t size
)
325 : mBuffer(NULL
), mRelease(false)
331 : mBuffer(NULL
), mRelease(false)
341 void Message::setBuffer(void *buffer
, mach_msg_size_t size
)
344 mBuffer
= reinterpret_cast<mig_reply_error_t
*>(buffer
);
349 void Message::setBuffer(mach_msg_size_t size
)
351 assert(size
>= sizeof(mach_msg_header_t
));
353 mSize
= size
+ MAX_TRAILER_SIZE
;
354 mBuffer
= reinterpret_cast<mig_reply_error_t
*>(new char[mSize
]());
358 void Message::clearBuffer(void)
360 (void)memset_s(mBuffer
, mSize
, 0, mSize
);
363 void Message::release()
366 delete[] reinterpret_cast<char *>(mBuffer
);
370 bool Message::check(kern_return_t status
)
375 case MACH_RCV_TIMED_OUT
:
376 case MACH_SEND_TIMED_OUT
:
379 Error::throwMe(status
);
384 bool Message::send(mach_msg_option_t options
,
385 mach_msg_timeout_t timeout
,
386 mach_port_name_t notify
)
388 return check(mach_msg_overwrite(*this,
389 options
| MACH_SEND_MSG
,
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
)
401 return check(mach_msg_overwrite(*this,
402 options
| MACH_RCV_MSG
,
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
)
414 return check(mach_msg_overwrite(*this,
415 options
| MACH_SEND_MSG
| MACH_RCV_MSG
,
422 /* cribbed from libdispatch: _dispatch_mach_msg_get_audit_trailer */
423 mach_msg_audit_trailer_t
*
424 Message::auditTrailer()
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
;
441 // Debug dumping of ports etc.
443 #if defined(DEBUGDUMP)
445 void Port::dump(const char *descr
)
447 if (mPort
== MACH_PORT_NULL
) {
448 Debug::dump("[%s==NULL]\n", descr
? descr
: "port");
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
));
455 if (type
& MACH_PORT_TYPE_SEND
)
456 Debug::dump(" send(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
457 if (type
& MACH_PORT_TYPE_RECEIVE
)
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
)
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
);
480 } // end namespace MachPlusPlus
481 } // end namespace Security