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