1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
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//
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>
34
35namespace Security {
36namespace MachPlusPlus {
37
38
39//
40// Mach subsystem exceptions, a subclass of CssmCommonError
41//
42Error::Error(kern_return_t err) : error(err)
43{
44	SECURITY_EXCEPTION_THROW_MACH(this, err);
45}
46
47Error::~Error() throw()
48{ }
49
50
51OSStatus Error::osStatus() const
52{
53	switch (error) {
54	case MIG_BAD_ARGUMENTS:
55	case MIG_TYPE_ERROR:
56	case MIG_REMOTE_ERROR:
57		return CSSMERR_CSSM_SERVICE_NOT_AVAILABLE;		// IPC mismatch of some sort
58	default:
59		return -1;		//@@@ some "internal error" code, perhaps?
60	}
61}
62
63int Error::unixError() const
64{
65	switch (error) {
66	case MIG_BAD_ARGUMENTS:
67	case MIG_TYPE_ERROR:
68	case MIG_REMOTE_ERROR:
69		return ERPCMISMATCH;		// IPC mismatch of some sort
70	default:
71		return -1;		//@@@ some "internal error" code, perhaps?
72	}
73}
74
75void Error::check(kern_return_t status)
76{
77	if (status != KERN_SUCCESS)
78		Error::throwMe(status);
79}
80
81void Error::throwMe(kern_return_t err)
82{
83	throw Error(err);
84}
85
86
87//
88// Memory management
89//
90void *allocate(size_t size)
91{
92	vm_address_t address;
93	check(vm_allocate(mach_task_self(), &address, size, true));
94	return reinterpret_cast<void *>(address);
95}
96
97void deallocate(vm_address_t address, size_t size)
98{
99	check(vm_deallocate(mach_task_self(), address, size));
100}
101
102
103//
104// Port functions
105//
106mach_port_urefs_t Port::getRefs(mach_port_right_t right)
107{
108	mach_port_urefs_t count;
109	check(::mach_port_get_refs(self(), mPort, right, &count));
110	return count;
111}
112
113mach_port_t Port::requestNotify(mach_port_t notify, mach_msg_id_t type, mach_port_mscount_t sync)
114{
115    mach_port_t previous;
116    check(mach_port_request_notification(self(), mPort, type, sync, notify,
117        MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous));
118
119#if !defined(NDEBUG)
120	const char *typeName;
121	switch (type) {
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;
128	}
129	if (notify == MACH_PORT_NULL)
130		secdebug("port", "%d cancel notify %s", port(), typeName);
131	else
132		secdebug("port", "%d request notify %s to %d (sync %d)", port(), typeName, notify, sync);
133#endif //!NDEBUG
134
135    return previous;
136}
137
138mach_port_t Port::cancelNotify(mach_msg_id_t type)
139{
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...
142    if (isDead())
143        return MACH_PORT_NULL;
144	return requestNotify(MACH_PORT_NULL, type);
145}
146
147mach_port_msgcount_t Port::qlimit() const
148{
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;
155}
156
157void Port::qlimit(mach_port_msgcount_t limit)
158{
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));
163}
164
165
166//
167// PortSet features
168//
169set<Port> PortSet::members() const
170{
171	mach_port_array_t members;
172	mach_msg_type_number_t count;
173	check(::mach_port_get_set_status(self(), mPort, &members, &count));
174	try {
175		set<Port> result;
176		copy(members, members+count, inserter(result, result.begin()));
177		vm_deallocate(self(), vm_address_t(members), count * sizeof(members[0]));
178		return result;
179	} catch (...) {
180		vm_deallocate(self(), vm_address_t(members), count * sizeof(members[0]));
181		throw;
182	}
183}
184
185
186bool PortSet::contains(Port member) const
187{
188	set<Port> memberSet = members();
189	return memberSet.find(member) != memberSet.end();
190}
191
192
193//
194// Task port features
195//
196TaskPort::TaskPort(pid_t pid)
197{
198	check(::task_for_pid(self(), pid, &mPort));
199}
200
201pid_t TaskPort::pid() const
202{
203    pid_t pid;
204    check(::pid_for_task(mPort, &pid));
205    return pid;
206}
207
208
209//
210// Bootstrap port management
211//
212mach_port_t Bootstrap::checkIn(const char *name) const
213{
214	mach_port_t port;
215	check(::bootstrap_check_in(mPort, makeName(name), &port));
216	return port;
217}
218
219mach_port_t Bootstrap::checkInOptional(const char *name) const
220{
221	mach_port_t port;
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;
227	default:
228		check(err);
229	}
230	return port;
231}
232
233void Bootstrap::registerAs(mach_port_t port, const char *name) const
234{
235	secdebug("bootstrap", "creating service port %d in %d:%s", port, this->port(), name);
236	check(::bootstrap_register(mPort, makeName(name), port));
237}
238
239mach_port_t Bootstrap::lookup(const char *name) const
240{
241	mach_port_t port;
242	check(::bootstrap_look_up(mPort, makeName(name), &port));
243	return port;
244}
245
246mach_port_t Bootstrap::lookup2(const char *name) const
247{
248	mach_port_t port;
249	check(::bootstrap_look_up2(mPort, makeName(name), &port, 0, BOOTSTRAP_PRIVILEGED_SERVER));
250	return port;
251}
252
253mach_port_t Bootstrap::lookupOptional(const char *name) const
254{
255	mach_port_t port;
256	kern_return_t err = ::bootstrap_look_up(mPort, makeName(name), &port);
257    if (err == BOOTSTRAP_UNKNOWN_SERVICE)
258        return 0;
259    check(err);
260	return port;
261}
262
263
264Bootstrap Bootstrap::subset(Port requestor)
265{
266    mach_port_t sub;
267    check(::bootstrap_subset(mPort, requestor, &sub));
268    return sub;
269}
270
271
272//
273// ReceivePorts
274//
275ReceivePort::ReceivePort(const char *name, const Bootstrap &bootstrap, bool tryCheckin /* = true */)
276{
277	if (tryCheckin)
278		mPort = bootstrap.checkInOptional(name);
279	if (!mPort) {
280		allocate();
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);
287	}
288}
289
290
291//
292// Stack-based bootstrap switcher
293//
294ModuleNexus<Mutex> StBootstrap::critical;
295
296StBootstrap::StBootstrap(const Bootstrap &newBoot, const TaskPort &task)
297    : mTask(task), locker(critical())
298{
299    mOldBoot = Bootstrap();
300    mTask.bootstrap(newBoot);
301    secdebug("StBoot", "bootstrap for %d switched to %d", mTask.port(), newBoot.port());
302}
303
304StBootstrap::~StBootstrap()
305{
306    mTask.bootstrap(mOldBoot);
307    secdebug("StBoot", "bootstrap for %d returned to %d", mTask.port(), mOldBoot.port());
308}
309
310
311//
312// Mach message buffers
313//
314Message::Message(void *buffer, mach_msg_size_t size)
315	: mBuffer(NULL), mRelease(false)
316{
317	setBuffer(buffer, size);
318}
319
320Message::Message(mach_msg_size_t size)
321	: mBuffer(NULL), mRelease(false)
322{
323	setBuffer(size);
324}
325
326Message::Message()
327	: mBuffer(NULL), mRelease(false)
328{ }
329
330
331Message::~Message()
332{
333	release();
334}
335
336
337void Message::setBuffer(void *buffer, mach_msg_size_t size)
338{
339	release();
340	mBuffer = reinterpret_cast<mig_reply_error_t *>(buffer);
341	mSize = size;
342	mRelease = false;
343}
344
345void Message::setBuffer(mach_msg_size_t size)
346{
347	assert(size >= sizeof(mach_msg_header_t));
348	release();
349	mSize = size + MAX_TRAILER_SIZE;
350	mBuffer = reinterpret_cast<mig_reply_error_t *>(new char[mSize]);
351	mRelease = true;
352}
353
354
355void Message::release()
356{
357	if (mRelease)
358		delete[] reinterpret_cast<char *>(mBuffer);
359}
360
361
362bool Message::check(kern_return_t status)
363{
364    switch (status) {
365    case KERN_SUCCESS:
366        return true;
367    case MACH_RCV_TIMED_OUT:
368    case MACH_SEND_TIMED_OUT:
369        return false;
370    default:
371        Error::throwMe(status);
372    }
373}
374
375
376bool Message::send(mach_msg_option_t options,
377    mach_msg_timeout_t timeout,
378    mach_port_name_t notify)
379{
380    return check(mach_msg_overwrite(*this,
381        options | MACH_SEND_MSG,
382        length(),
383        0, MACH_PORT_NULL,
384        timeout, notify,
385        NULL, 0));
386}
387
388bool Message::receive(mach_port_t receivePort,
389    mach_msg_option_t options,
390    mach_msg_timeout_t timeout,
391    mach_port_name_t notify)
392{
393    return check(mach_msg_overwrite(*this,
394        options | MACH_RCV_MSG,
395        length(),
396        mSize, receivePort,
397        timeout, notify,
398        NULL, 0));
399}
400
401bool Message::sendReceive(mach_port_t receivePort,
402    mach_msg_option_t options,
403    mach_msg_timeout_t timeout,
404    mach_port_name_t notify)
405{
406    return check(mach_msg_overwrite(*this,
407        options | MACH_SEND_MSG | MACH_RCV_MSG,
408        length(),
409        mSize, receivePort,
410        timeout, notify,
411        NULL, 0));
412}
413
414
415//
416// Debug dumping of ports etc.
417//
418#if defined(DEBUGDUMP)
419
420void Port::dump(const char *descr)
421{
422    if (mPort == MACH_PORT_NULL) {
423        Debug::dump("[%s==NULL]\n", descr ? descr : "port");
424    } else {
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));
429        } else {
430            if (type & MACH_PORT_TYPE_SEND)
431                Debug::dump(" send(%d)", getRefs(MACH_PORT_RIGHT_SEND));
432            if (type & MACH_PORT_TYPE_RECEIVE)
433                Debug::dump(" rcv");
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)
437                Debug::dump(" 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);
446        }
447        Debug::dump("]\n");
448    }
449}
450
451
452#endif //DEBUGDUMP
453
454
455} // end namespace MachPlusPlus
456} // end namespace Security
457