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() throw()
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
;
147 return requestNotify(MACH_PORT_NULL
, type
);
150 mach_port_msgcount_t
Port::qlimit() const
152 mach_port_limits_t limits
;
153 mach_msg_type_number_t infoCount
= 1;
154 check(::mach_port_get_attributes(self(), mPort
, MACH_PORT_LIMITS_INFO
,
155 mach_port_info_t(&limits
), &infoCount
));
156 assert(infoCount
== 1);
157 return limits
.mpl_qlimit
;
160 void Port::qlimit(mach_port_msgcount_t limit
)
162 mach_port_limits_t limits
;
163 limits
.mpl_qlimit
= limit
;
164 check(::mach_port_set_attributes(self(), mPort
, MACH_PORT_LIMITS_INFO
,
165 mach_port_info_t(&limits
), MACH_PORT_LIMITS_INFO_COUNT
));
172 set
<Port
> PortSet::members() const
174 mach_port_array_t members
;
175 mach_msg_type_number_t count
;
176 check(::mach_port_get_set_status(self(), mPort
, &members
, &count
));
179 copy(members
, members
+count
, inserter(result
, result
.begin()));
180 vm_deallocate(self(), vm_address_t(members
), count
* sizeof(members
[0]));
183 vm_deallocate(self(), vm_address_t(members
), count
* sizeof(members
[0]));
189 bool PortSet::contains(Port member
) const
191 set
<Port
> memberSet
= members();
192 return memberSet
.find(member
) != memberSet
.end();
197 // Task port features
199 TaskPort::TaskPort(pid_t pid
)
201 check(::task_for_pid(self(), pid
, &mPort
));
204 pid_t
TaskPort::pid() const
207 check(::pid_for_task(mPort
, &pid
));
213 // Bootstrap port management
215 mach_port_t
Bootstrap::checkIn(const char *name
) const
218 check(::bootstrap_check_in(mPort
, makeName(name
), &port
));
222 mach_port_t
Bootstrap::checkInOptional(const char *name
) const
225 switch (kern_return_t err
= ::bootstrap_check_in(mPort
, makeName(name
), &port
)) {
226 case BOOTSTRAP_SERVICE_ACTIVE
:
227 case BOOTSTRAP_UNKNOWN_SERVICE
:
228 case BOOTSTRAP_NOT_PRIVILEGED
:
229 return MACH_PORT_NULL
;
236 void Bootstrap::registerAs(mach_port_t port
, const char *name
) const
238 secinfo("bootstrap", "creating service port %d in %d:%s", port
, this->port(), name
);
239 check(::bootstrap_register(mPort
, makeName(name
), port
));
242 mach_port_t
Bootstrap::lookup(const char *name
) const
245 check(::bootstrap_look_up(mPort
, makeName(name
), &port
));
249 mach_port_t
Bootstrap::lookup2(const char *name
) const
252 check(::bootstrap_look_up2(mPort
, makeName(name
), &port
, 0, BOOTSTRAP_PRIVILEGED_SERVER
));
256 mach_port_t
Bootstrap::lookupOptional(const char *name
) const
259 kern_return_t err
= ::bootstrap_look_up(mPort
, makeName(name
), &port
);
260 if (err
== BOOTSTRAP_UNKNOWN_SERVICE
)
267 Bootstrap
Bootstrap::subset(Port requestor
)
270 check(::bootstrap_subset(mPort
, requestor
, &sub
));
278 ReceivePort::ReceivePort(const char *name
, const Bootstrap
&bootstrap
, bool tryCheckin
/* = true */)
281 mPort
= bootstrap
.checkInOptional(name
);
284 // Bootstrap registration requires a send right to (copy) send.
285 // Make a temporary one, send it, then take it away again, to avoid
286 // messing up the caller's send right accounting.
287 insertRight(MACH_MSG_TYPE_MAKE_SEND
);
288 bootstrap
.registerAs(mPort
, name
);
289 modRefs(MACH_PORT_RIGHT_SEND
, -1);
295 // Stack-based bootstrap switcher
297 ModuleNexus
<Mutex
> StBootstrap::critical
;
299 StBootstrap::StBootstrap(const Bootstrap
&newBoot
, const TaskPort
&task
)
300 : mTask(task
), locker(critical())
302 mOldBoot
= Bootstrap();
303 mTask
.bootstrap(newBoot
);
304 secinfo("StBoot", "bootstrap for %d switched to %d", mTask
.port(), newBoot
.port());
307 StBootstrap::~StBootstrap()
309 mTask
.bootstrap(mOldBoot
);
310 secinfo("StBoot", "bootstrap for %d returned to %d", mTask
.port(), mOldBoot
.port());
315 // Mach message buffers
317 Message::Message(void *buffer
, mach_msg_size_t size
)
318 : mBuffer(NULL
), mRelease(false)
320 setBuffer(buffer
, size
);
323 Message::Message(mach_msg_size_t size
)
324 : mBuffer(NULL
), mRelease(false)
330 : mBuffer(NULL
), mRelease(false)
340 void Message::setBuffer(void *buffer
, mach_msg_size_t size
)
343 mBuffer
= reinterpret_cast<mig_reply_error_t
*>(buffer
);
348 void Message::setBuffer(mach_msg_size_t size
)
350 assert(size
>= sizeof(mach_msg_header_t
));
352 mSize
= size
+ MAX_TRAILER_SIZE
;
353 mBuffer
= reinterpret_cast<mig_reply_error_t
*>(new char[mSize
]());
357 void Message::clearBuffer(void)
359 (void)memset_s(mBuffer
, mSize
, 0, mSize
);
362 void Message::release()
365 delete[] reinterpret_cast<char *>(mBuffer
);
369 bool Message::check(kern_return_t status
)
374 case MACH_RCV_TIMED_OUT
:
375 case MACH_SEND_TIMED_OUT
:
378 Error::throwMe(status
);
383 bool Message::send(mach_msg_option_t options
,
384 mach_msg_timeout_t timeout
,
385 mach_port_name_t notify
)
387 return check(mach_msg_overwrite(*this,
388 options
| MACH_SEND_MSG
,
395 bool Message::receive(mach_port_t receivePort
,
396 mach_msg_option_t options
,
397 mach_msg_timeout_t timeout
,
398 mach_port_name_t notify
)
400 return check(mach_msg_overwrite(*this,
401 options
| MACH_RCV_MSG
,
408 bool Message::sendReceive(mach_port_t receivePort
,
409 mach_msg_option_t options
,
410 mach_msg_timeout_t timeout
,
411 mach_port_name_t notify
)
413 return check(mach_msg_overwrite(*this,
414 options
| MACH_SEND_MSG
| MACH_RCV_MSG
,
421 /* cribbed from libdispatch: _dispatch_mach_msg_get_audit_trailer */
422 mach_msg_audit_trailer_t
*
423 Message::auditTrailer()
425 mach_msg_header_t
*hdr
= &mBuffer
->Head
;
426 mach_msg_trailer_t
*tlr
= NULL
;
427 mach_msg_audit_trailer_t
*audit_tlr
= NULL
;
428 tlr
= (mach_msg_trailer_t
*)((unsigned char *)hdr
+
429 round_msg(hdr
->msgh_size
));
430 // The trailer should always be of format zero.
431 if (tlr
->msgh_trailer_type
== MACH_MSG_TRAILER_FORMAT_0
) {
432 if (tlr
->msgh_trailer_size
>= sizeof(mach_msg_audit_trailer_t
)) {
433 audit_tlr
= (mach_msg_audit_trailer_t
*)tlr
;
440 // Debug dumping of ports etc.
442 #if defined(DEBUGDUMP)
444 void Port::dump(const char *descr
)
446 if (mPort
== MACH_PORT_NULL
) {
447 Debug::dump("[%s==NULL]\n", descr
? descr
: "port");
449 Debug::dump("[%s(%d)", descr
? descr
: "port", mPort
);
450 mach_port_type_t type
;
451 if (kern_return_t err
= mach_port_type(self(), mPort
, &type
)) {
452 Debug::dump(" !%s", mach_error_string(err
));
454 if (type
& MACH_PORT_TYPE_SEND
)
455 Debug::dump(" send(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
456 if (type
& MACH_PORT_TYPE_RECEIVE
)
458 if (type
& MACH_PORT_TYPE_SEND_ONCE
)
459 Debug::dump(" once(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
460 if (type
& MACH_PORT_TYPE_PORT_SET
)
462 if (type
& MACH_PORT_TYPE_DEAD_NAME
)
463 Debug::dump(" dead(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
464 if (type
& MACH_PORT_TYPE_DNREQUEST
)
465 Debug::dump(" dnreq");
466 // handle unknown/unexpected type flags
467 if (type
& ~(MACH_PORT_TYPE_SEND
|MACH_PORT_TYPE_RECEIVE
|MACH_PORT_TYPE_SEND_ONCE
|
468 MACH_PORT_TYPE_PORT_SET
|MACH_PORT_TYPE_DEAD_NAME
|MACH_PORT_TYPE_DNREQUEST
))
469 Debug::dump(" type(0x%x)", type
);
479 } // end namespace MachPlusPlus
480 } // end namespace Security