]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_utilities/lib/mach++.cpp
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / mach++.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2000-2004,2011-2012,2014 Apple Inc. All Rights Reserved.
b1ab9ed8
A
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>
b1ab9ed8
A
31#include <servers/bootstrap_defs.h> // debug
32#include <bootstrap_priv.h>
33
34namespace Security {
35namespace MachPlusPlus {
36
37
38//
39// Mach subsystem exceptions, a subclass of CssmCommonError
40//
41Error::Error(kern_return_t err) : error(err)
42{
fa7225c8
A
43 SECURITY_EXCEPTION_THROW_MACH(this, err);
44 secnotice("security_exception", "mach error: %d", err);
b1ab9ed8
A
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:
866f8763 57 return errSecServiceNotAvailable; // IPC mismatch of some sort
b1ab9ed8
A
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)
fa7225c8 130 secinfo("port", "%d cancel notify %s", port(), typeName);
b1ab9ed8 131 else
fa7225c8 132 secinfo("port", "%d request notify %s to %d (sync %d)", port(), typeName, notify, sync);
b1ab9ed8
A
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{
fa7225c8 235 secinfo("bootstrap", "creating service port %d in %d:%s", port, this->port(), name);
b1ab9ed8
A
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);
fa7225c8 301 secinfo("StBoot", "bootstrap for %d switched to %d", mTask.port(), newBoot.port());
b1ab9ed8
A
302}
303
304StBootstrap::~StBootstrap()
305{
306 mTask.bootstrap(mOldBoot);
fa7225c8 307 secinfo("StBoot", "bootstrap for %d returned to %d", mTask.port(), mOldBoot.port());
b1ab9ed8
A
308}
309
310
311//
312// Mach message buffers
313//
427c49bc 314Message::Message(void *buffer, mach_msg_size_t size)
b1ab9ed8
A
315 : mBuffer(NULL), mRelease(false)
316{
317 setBuffer(buffer, size);
318}
319
427c49bc 320Message::Message(mach_msg_size_t size)
b1ab9ed8
A
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
427c49bc 337void Message::setBuffer(void *buffer, mach_msg_size_t size)
b1ab9ed8
A
338{
339 release();
340 mBuffer = reinterpret_cast<mig_reply_error_t *>(buffer);
341 mSize = size;
342 mRelease = false;
343}
344
427c49bc 345void Message::setBuffer(mach_msg_size_t size)
b1ab9ed8
A
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