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
);
45 secnotice("security_exception", "mach error: %d", err
);
48 Error::~Error() throw()
52 OSStatus
Error::osStatus() const
55 case MIG_BAD_ARGUMENTS
:
57 case MIG_REMOTE_ERROR
:
58 return CSSMERR_CSSM_SERVICE_NOT_AVAILABLE
; // IPC mismatch of some sort
60 return -1; //@@@ some "internal error" code, perhaps?
64 int Error::unixError() const
67 case MIG_BAD_ARGUMENTS
:
69 case MIG_REMOTE_ERROR
:
70 return ERPCMISMATCH
; // IPC mismatch of some sort
72 return -1; //@@@ some "internal error" code, perhaps?
76 void Error::check(kern_return_t status
)
78 if (status
!= KERN_SUCCESS
)
79 Error::throwMe(status
);
82 void Error::throwMe(kern_return_t err
)
91 void *allocate(size_t size
)
94 check(vm_allocate(mach_task_self(), &address
, size
, true));
95 return reinterpret_cast<void *>(address
);
98 void deallocate(vm_address_t address
, size_t size
)
100 check(vm_deallocate(mach_task_self(), address
, size
));
107 mach_port_urefs_t
Port::getRefs(mach_port_right_t right
)
109 mach_port_urefs_t count
;
110 check(::mach_port_get_refs(self(), mPort
, right
, &count
));
114 mach_port_t
Port::requestNotify(mach_port_t notify
, mach_msg_id_t type
, mach_port_mscount_t sync
)
116 mach_port_t previous
;
117 check(mach_port_request_notification(self(), mPort
, type
, sync
, notify
,
118 MACH_MSG_TYPE_MAKE_SEND_ONCE
, &previous
));
121 const char *typeName
;
123 case MACH_NOTIFY_PORT_DELETED
: typeName
= "port deleted"; break;
124 case MACH_NOTIFY_PORT_DESTROYED
:typeName
= "port destroyed"; break;
125 case MACH_NOTIFY_NO_SENDERS
: typeName
= "no senders"; break;
126 case MACH_NOTIFY_SEND_ONCE
: typeName
= "send once"; break;
127 case MACH_NOTIFY_DEAD_NAME
: typeName
= "dead name"; break;
128 default: typeName
= "???"; break;
130 if (notify
== MACH_PORT_NULL
)
131 secinfo("port", "%d cancel notify %s", port(), typeName
);
133 secinfo("port", "%d request notify %s to %d (sync %d)", port(), typeName
, notify
, sync
);
139 mach_port_t
Port::cancelNotify(mach_msg_id_t type
)
141 // Mach won't let us unset the DPN port if we are already dead
142 // (EVEN if the DPN has already been sent!) So just ignore that case...
144 return MACH_PORT_NULL
;
145 return requestNotify(MACH_PORT_NULL
, type
);
148 mach_port_msgcount_t
Port::qlimit() const
150 mach_port_limits_t limits
;
151 mach_msg_type_number_t infoCount
= 1;
152 check(::mach_port_get_attributes(self(), mPort
, MACH_PORT_LIMITS_INFO
,
153 mach_port_info_t(&limits
), &infoCount
));
154 assert(infoCount
== 1);
155 return limits
.mpl_qlimit
;
158 void Port::qlimit(mach_port_msgcount_t limit
)
160 mach_port_limits_t limits
;
161 limits
.mpl_qlimit
= limit
;
162 check(::mach_port_set_attributes(self(), mPort
, MACH_PORT_LIMITS_INFO
,
163 mach_port_info_t(&limits
), MACH_PORT_LIMITS_INFO_COUNT
));
170 set
<Port
> PortSet::members() const
172 mach_port_array_t members
;
173 mach_msg_type_number_t count
;
174 check(::mach_port_get_set_status(self(), mPort
, &members
, &count
));
177 copy(members
, members
+count
, inserter(result
, result
.begin()));
178 vm_deallocate(self(), vm_address_t(members
), count
* sizeof(members
[0]));
181 vm_deallocate(self(), vm_address_t(members
), count
* sizeof(members
[0]));
187 bool PortSet::contains(Port member
) const
189 set
<Port
> memberSet
= members();
190 return memberSet
.find(member
) != memberSet
.end();
195 // Task port features
197 TaskPort::TaskPort(pid_t pid
)
199 check(::task_for_pid(self(), pid
, &mPort
));
202 pid_t
TaskPort::pid() const
205 check(::pid_for_task(mPort
, &pid
));
211 // Bootstrap port management
213 mach_port_t
Bootstrap::checkIn(const char *name
) const
216 check(::bootstrap_check_in(mPort
, makeName(name
), &port
));
220 mach_port_t
Bootstrap::checkInOptional(const char *name
) const
223 switch (kern_return_t err
= ::bootstrap_check_in(mPort
, makeName(name
), &port
)) {
224 case BOOTSTRAP_SERVICE_ACTIVE
:
225 case BOOTSTRAP_UNKNOWN_SERVICE
:
226 case BOOTSTRAP_NOT_PRIVILEGED
:
227 return MACH_PORT_NULL
;
234 void Bootstrap::registerAs(mach_port_t port
, const char *name
) const
236 secinfo("bootstrap", "creating service port %d in %d:%s", port
, this->port(), name
);
237 check(::bootstrap_register(mPort
, makeName(name
), port
));
240 mach_port_t
Bootstrap::lookup(const char *name
) const
243 check(::bootstrap_look_up(mPort
, makeName(name
), &port
));
247 mach_port_t
Bootstrap::lookup2(const char *name
) const
250 check(::bootstrap_look_up2(mPort
, makeName(name
), &port
, 0, BOOTSTRAP_PRIVILEGED_SERVER
));
254 mach_port_t
Bootstrap::lookupOptional(const char *name
) const
257 kern_return_t err
= ::bootstrap_look_up(mPort
, makeName(name
), &port
);
258 if (err
== BOOTSTRAP_UNKNOWN_SERVICE
)
265 Bootstrap
Bootstrap::subset(Port requestor
)
268 check(::bootstrap_subset(mPort
, requestor
, &sub
));
276 ReceivePort::ReceivePort(const char *name
, const Bootstrap
&bootstrap
, bool tryCheckin
/* = true */)
279 mPort
= bootstrap
.checkInOptional(name
);
282 // Bootstrap registration requires a send right to (copy) send.
283 // Make a temporary one, send it, then take it away again, to avoid
284 // messing up the caller's send right accounting.
285 insertRight(MACH_MSG_TYPE_MAKE_SEND
);
286 bootstrap
.registerAs(mPort
, name
);
287 modRefs(MACH_PORT_RIGHT_SEND
, -1);
293 // Stack-based bootstrap switcher
295 ModuleNexus
<Mutex
> StBootstrap::critical
;
297 StBootstrap::StBootstrap(const Bootstrap
&newBoot
, const TaskPort
&task
)
298 : mTask(task
), locker(critical())
300 mOldBoot
= Bootstrap();
301 mTask
.bootstrap(newBoot
);
302 secinfo("StBoot", "bootstrap for %d switched to %d", mTask
.port(), newBoot
.port());
305 StBootstrap::~StBootstrap()
307 mTask
.bootstrap(mOldBoot
);
308 secinfo("StBoot", "bootstrap for %d returned to %d", mTask
.port(), mOldBoot
.port());
313 // Mach message buffers
315 Message::Message(void *buffer
, mach_msg_size_t size
)
316 : mBuffer(NULL
), mRelease(false)
318 setBuffer(buffer
, size
);
321 Message::Message(mach_msg_size_t size
)
322 : mBuffer(NULL
), mRelease(false)
328 : mBuffer(NULL
), mRelease(false)
338 void Message::setBuffer(void *buffer
, mach_msg_size_t size
)
341 mBuffer
= reinterpret_cast<mig_reply_error_t
*>(buffer
);
346 void Message::setBuffer(mach_msg_size_t size
)
348 assert(size
>= sizeof(mach_msg_header_t
));
350 mSize
= size
+ MAX_TRAILER_SIZE
;
351 mBuffer
= reinterpret_cast<mig_reply_error_t
*>(new char[mSize
]);
356 void Message::release()
359 delete[] reinterpret_cast<char *>(mBuffer
);
363 bool Message::check(kern_return_t status
)
368 case MACH_RCV_TIMED_OUT
:
369 case MACH_SEND_TIMED_OUT
:
372 Error::throwMe(status
);
377 bool Message::send(mach_msg_option_t options
,
378 mach_msg_timeout_t timeout
,
379 mach_port_name_t notify
)
381 return check(mach_msg_overwrite(*this,
382 options
| MACH_SEND_MSG
,
389 bool Message::receive(mach_port_t receivePort
,
390 mach_msg_option_t options
,
391 mach_msg_timeout_t timeout
,
392 mach_port_name_t notify
)
394 return check(mach_msg_overwrite(*this,
395 options
| MACH_RCV_MSG
,
402 bool Message::sendReceive(mach_port_t receivePort
,
403 mach_msg_option_t options
,
404 mach_msg_timeout_t timeout
,
405 mach_port_name_t notify
)
407 return check(mach_msg_overwrite(*this,
408 options
| MACH_SEND_MSG
| MACH_RCV_MSG
,
417 // Debug dumping of ports etc.
419 #if defined(DEBUGDUMP)
421 void Port::dump(const char *descr
)
423 if (mPort
== MACH_PORT_NULL
) {
424 Debug::dump("[%s==NULL]\n", descr
? descr
: "port");
426 Debug::dump("[%s(%d)", descr
? descr
: "port", mPort
);
427 mach_port_type_t type
;
428 if (kern_return_t err
= mach_port_type(self(), mPort
, &type
)) {
429 Debug::dump(" !%s", mach_error_string(err
));
431 if (type
& MACH_PORT_TYPE_SEND
)
432 Debug::dump(" send(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
433 if (type
& MACH_PORT_TYPE_RECEIVE
)
435 if (type
& MACH_PORT_TYPE_SEND_ONCE
)
436 Debug::dump(" once(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
437 if (type
& MACH_PORT_TYPE_PORT_SET
)
439 if (type
& MACH_PORT_TYPE_DEAD_NAME
)
440 Debug::dump(" dead(%d)", getRefs(MACH_PORT_RIGHT_SEND
));
441 if (type
& MACH_PORT_TYPE_DNREQUEST
)
442 Debug::dump(" dnreq");
443 // handle unknown/unexpected type flags
444 if (type
& ~(MACH_PORT_TYPE_SEND
|MACH_PORT_TYPE_RECEIVE
|MACH_PORT_TYPE_SEND_ONCE
|
445 MACH_PORT_TYPE_PORT_SET
|MACH_PORT_TYPE_DEAD_NAME
|MACH_PORT_TYPE_DNREQUEST
))
446 Debug::dump(" type(0x%x)", type
);
456 } // end namespace MachPlusPlus
457 } // end namespace Security