]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/mach++.cpp
Security-57337.20.44.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 }
46
47 Error::~Error() throw()
48 { }
49
50
51 OSStatus 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
63 int 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
75 void Error::check(kern_return_t status)
76 {
77 if (status != KERN_SUCCESS)
78 Error::throwMe(status);
79 }
80
81 void Error::throwMe(kern_return_t err)
82 {
83 throw Error(err);
84 }
85
86
87 //
88 // Memory management
89 //
90 void *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
97 void 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 //
106 mach_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
113 mach_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
138 mach_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
147 mach_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
157 void 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 //
169 set<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
186 bool 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 //
196 TaskPort::TaskPort(pid_t pid)
197 {
198 check(::task_for_pid(self(), pid, &mPort));
199 }
200
201 pid_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 //
212 mach_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
219 mach_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
233 void 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
239 mach_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
246 mach_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
253 mach_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
264 Bootstrap 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 //
275 ReceivePort::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 //
294 ModuleNexus<Mutex> StBootstrap::critical;
295
296 StBootstrap::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
304 StBootstrap::~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 //
314 Message::Message(void *buffer, mach_msg_size_t size)
315 : mBuffer(NULL), mRelease(false)
316 {
317 setBuffer(buffer, size);
318 }
319
320 Message::Message(mach_msg_size_t size)
321 : mBuffer(NULL), mRelease(false)
322 {
323 setBuffer(size);
324 }
325
326 Message::Message()
327 : mBuffer(NULL), mRelease(false)
328 { }
329
330
331 Message::~Message()
332 {
333 release();
334 }
335
336
337 void 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
345 void 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
355 void Message::release()
356 {
357 if (mRelease)
358 delete[] reinterpret_cast<char *>(mBuffer);
359 }
360
361
362 bool 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
376 bool 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
388 bool 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
401 bool 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
420 void 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