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