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