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 #include <security_utilities/mach++.h>
29 #include <mach/mach_error.h>
30 #include <security_utilities/debugging.h>
31 #include <Security/cssmapple.h> // error codes
32 #include <servers/bootstrap_defs.h> // debug
33 #include <bootstrap_priv.h>
36 namespace MachPlusPlus
{
40 // Mach subsystem exceptions, a subclass of CssmCommonError
42 Error::Error(kern_return_t err
) : error(err
)
44 SECURITY_EXCEPTION_THROW_MACH(this, err
);
47 Error::~Error() throw()
51 OSStatus
Error::osStatus() const
54 case MIG_BAD_ARGUMENTS
:
56 case MIG_REMOTE_ERROR
:
57 return CSSMERR_CSSM_SERVICE_NOT_AVAILABLE
; // IPC mismatch of some sort
59 return -1; //@@@ some "internal error" code, perhaps?
63 int Error::unixError() const
66 case MIG_BAD_ARGUMENTS
:
68 case MIG_REMOTE_ERROR
:
69 return ERPCMISMATCH
; // IPC mismatch of some sort
71 return -1; //@@@ some "internal error" code, perhaps?
75 void Error::check(kern_return_t status
)
77 if (status
!= KERN_SUCCESS
)
78 Error::throwMe(status
);
81 void Error::throwMe(kern_return_t err
)
90 void *allocate(size_t size
)
93 check(vm_allocate(mach_task_self(), &address
, size
, true));
94 return reinterpret_cast<void *>(address
);
97 void deallocate(vm_address_t address
, size_t size
)
99 check(vm_deallocate(mach_task_self(), address
, size
));
106 mach_port_urefs_t
Port::getRefs(mach_port_right_t right
)
108 mach_port_urefs_t count
;
109 check(::mach_port_get_refs(self(), mPort
, right
, &count
));
113 mach_port_t
Port::requestNotify(mach_port_t notify
, mach_msg_id_t type
, mach_port_mscount_t sync
)
115 mach_port_t previous
;
116 check(mach_port_request_notification(self(), mPort
, type
, sync
, notify
,
117 MACH_MSG_TYPE_MAKE_SEND_ONCE
, &previous
));
120 const char *typeName
;
122 case MACH_NOTIFY_PORT_DELETED
: typeName
= "port deleted"; break;
123 case MACH_NOTIFY_PORT_DESTROYED
:typeName
= "port destroyed"; break;
124 case MACH_NOTIFY_NO_SENDERS
: typeName
= "no senders"; break;
125 case MACH_NOTIFY_SEND_ONCE
: typeName
= "send once"; break;
126 case MACH_NOTIFY_DEAD_NAME
: typeName
= "dead name"; break;
127 default: typeName
= "???"; break;
129 if (notify
== MACH_PORT_NULL
)
130 secdebug("port", "%d cancel notify %s", port(), typeName
);
132 secdebug("port", "%d request notify %s to %d (sync %d)", port(), typeName
, notify
, sync
);
138 mach_port_t
Port::cancelNotify(mach_msg_id_t type
)
140 // Mach won't let us unset the DPN port if we are already dead
141 // (EVEN if the DPN has already been sent!) So just ignore that case...
143 return MACH_PORT_NULL
;
144 return requestNotify(MACH_PORT_NULL
, type
);
147 mach_port_msgcount_t
Port::qlimit() const
149 mach_port_limits_t limits
;
150 mach_msg_type_number_t infoCount
= 1;
151 check(::mach_port_get_attributes(self(), mPort
, MACH_PORT_LIMITS_INFO
,
152 mach_port_info_t(&limits
), &infoCount
));
153 assert(infoCount
== 1);
154 return limits
.mpl_qlimit
;
157 void Port::qlimit(mach_port_msgcount_t limit
)
159 mach_port_limits_t limits
;
160 limits
.mpl_qlimit
= limit
;
161 check(::mach_port_set_attributes(self(), mPort
, MACH_PORT_LIMITS_INFO
,
162 mach_port_info_t(&limits
), MACH_PORT_LIMITS_INFO_COUNT
));
169 set
<Port
> PortSet::members() const
171 mach_port_array_t members
;
172 mach_msg_type_number_t count
;
173 check(::mach_port_get_set_status(self(), mPort
, &members
, &count
));
176 copy(members
, members
+count
, inserter(result
, result
.begin()));
177 vm_deallocate(self(), vm_address_t(members
), count
* sizeof(members
[0]));
180 vm_deallocate(self(), vm_address_t(members
), count
* sizeof(members
[0]));
186 bool PortSet::contains(Port member
) const
188 set
<Port
> memberSet
= members();
189 return memberSet
.find(member
) != memberSet
.end();
194 // Task port features
196 TaskPort::TaskPort(pid_t pid
)
198 check(::task_for_pid(self(), pid
, &mPort
));
201 pid_t
TaskPort::pid() const
204 check(::pid_for_task(mPort
, &pid
));
210 // Bootstrap port management
212 mach_port_t
Bootstrap::checkIn(const char *name
) const
215 check(::bootstrap_check_in(mPort
, makeName(name
), &port
));
219 mach_port_t
Bootstrap::checkInOptional(const char *name
) const
222 switch (kern_return_t err
= ::bootstrap_check_in(mPort
, makeName(name
), &port
)) {
223 case BOOTSTRAP_SERVICE_ACTIVE
:
224 case BOOTSTRAP_UNKNOWN_SERVICE
:
225 case BOOTSTRAP_NOT_PRIVILEGED
:
226 return MACH_PORT_NULL
;
233 void Bootstrap::registerAs(mach_port_t port
, const char *name
) const
235 secdebug("bootstrap", "creating service port %d in %d:%s", port
, this->port(), name
);
236 check(::bootstrap_register(mPort
, makeName(name
), port
));
239 mach_port_t
Bootstrap::lookup(const char *name
) const
242 check(::bootstrap_look_up(mPort
, makeName(name
), &port
));
246 mach_port_t
Bootstrap::lookup2(const char *name
) const
249 check(::bootstrap_look_up2(mPort
, makeName(name
), &port
, 0, BOOTSTRAP_PRIVILEGED_SERVER
));
253 mach_port_t
Bootstrap::lookupOptional(const char *name
) const
256 kern_return_t err
= ::bootstrap_look_up(mPort
, makeName(name
), &port
);
257 if (err
== BOOTSTRAP_UNKNOWN_SERVICE
)
264 Bootstrap
Bootstrap::subset(Port requestor
)
267 check(::bootstrap_subset(mPort
, requestor
, &sub
));
275 ReceivePort::ReceivePort(const char *name
, const Bootstrap
&bootstrap
, bool tryCheckin
/* = true */)
278 mPort
= bootstrap
.checkInOptional(name
);
281 // Bootstrap registration requires a send right to (copy) send.
282 // Make a temporary one, send it, then take it away again, to avoid
283 // messing up the caller's send right accounting.
284 insertRight(MACH_MSG_TYPE_MAKE_SEND
);
285 bootstrap
.registerAs(mPort
, name
);
286 modRefs(MACH_PORT_RIGHT_SEND
, -1);
292 // Stack-based bootstrap switcher
294 ModuleNexus
<Mutex
> StBootstrap::critical
;
296 StBootstrap::StBootstrap(const Bootstrap
&newBoot
, const TaskPort
&task
)
297 : mTask(task
), locker(critical())
299 mOldBoot
= Bootstrap();
300 mTask
.bootstrap(newBoot
);
301 secdebug("StBoot", "bootstrap for %d switched to %d", mTask
.port(), newBoot
.port());
304 StBootstrap::~StBootstrap()
306 mTask
.bootstrap(mOldBoot
);
307 secdebug("StBoot", "bootstrap for %d returned to %d", mTask
.port(), mOldBoot
.port());
312 // Mach message buffers
314 Message::Message(void *buffer
, mach_msg_size_t size
)
315 : mBuffer(NULL
), mRelease(false)
317 setBuffer(buffer
, size
);
320 Message::Message(mach_msg_size_t size
)
321 : mBuffer(NULL
), mRelease(false)
327 : mBuffer(NULL
), mRelease(false)
337 void Message::setBuffer(void *buffer
, mach_msg_size_t size
)
340 mBuffer
= reinterpret_cast<mig_reply_error_t
*>(buffer
);
345 void Message::setBuffer(mach_msg_size_t size
)
347 assert(size
>= sizeof(mach_msg_header_t
));
349 mSize
= size
+ MAX_TRAILER_SIZE
;
350 mBuffer
= reinterpret_cast<mig_reply_error_t
*>(new char[mSize
]);
355 void Message::release()
358 delete[] reinterpret_cast<char *>(mBuffer
);
362 bool Message::check(kern_return_t status
)
367 case MACH_RCV_TIMED_OUT
:
368 case MACH_SEND_TIMED_OUT
:
371 Error::throwMe(status
);
376 bool Message::send(mach_msg_option_t options
,
377 mach_msg_timeout_t timeout
,
378 mach_port_name_t notify
)
380 return check(mach_msg_overwrite(*this,
381 options
| MACH_SEND_MSG
,
388 bool Message::receive(mach_port_t receivePort
,
389 mach_msg_option_t options
,
390 mach_msg_timeout_t timeout
,
391 mach_port_name_t notify
)
393 return check(mach_msg_overwrite(*this,
394 options
| MACH_RCV_MSG
,
401 bool Message::sendReceive(mach_port_t receivePort
,
402 mach_msg_option_t options
,
403 mach_msg_timeout_t timeout
,
404 mach_port_name_t notify
)
406 return check(mach_msg_overwrite(*this,
407 options
| MACH_SEND_MSG
| MACH_RCV_MSG
,
416 // Debug dumping of ports etc.
418 #if defined(DEBUGDUMP)
420 void Port::dump(const char *descr
)
422 if (mPort
== MACH_PORT_NULL
) {
423 Debug::dump("[%s==NULL]\n", descr
? descr
: "port");
425 Debug::dump("[%s(%d)", descr
? descr
: "port", mPort
);
426 mach_port_type_t type
;
427 if (kern_return_t err
= mach_port_type(self(), mPort
, &type
)) {
428 Debug::dump(" !%s", mach_error_string(err
));
430 if (type
& MACH_PORT_TYPE_SEND
)
431 Debug::dump(" send(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
432 if (type
& MACH_PORT_TYPE_RECEIVE
)
434 if (type
& MACH_PORT_TYPE_SEND_ONCE
)
435 Debug::dump(" once(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
436 if (type
& MACH_PORT_TYPE_PORT_SET
)
438 if (type
& MACH_PORT_TYPE_DEAD_NAME
)
439 Debug::dump(" dead(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
440 if (type
& MACH_PORT_TYPE_DNREQUEST
)
441 Debug::dump(" dnreq");
442 // handle unknown/unexpected type flags
443 if (type
& ~(MACH_PORT_TYPE_SEND
|MACH_PORT_TYPE_RECEIVE
|MACH_PORT_TYPE_SEND_ONCE
|
444 MACH_PORT_TYPE_PORT_SET
|MACH_PORT_TYPE_DEAD_NAME
|MACH_PORT_TYPE_DNREQUEST
))
445 Debug::dump(" type(0x%x)", type
);
455 } // end namespace MachPlusPlus
456 } // end namespace Security