2  * Copyright (c) 2000-2004,2011-2012,2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  26 // mach++ - C++ bindings for useful Mach primitives 
  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> 
  36 namespace MachPlusPlus 
{ 
  40 // Mach subsystem exceptions, a subclass of CssmCommonError 
  42 Error::Error(kern_return_t err
) : error(err
) 
  44     SECURITY_EXCEPTION_THROW_MACH(this, err
); 
  45     secnotice("security_exception", "mach error: %d", err
); 
  48 Error::~Error() throw() 
  52 OSStatus 
Error::osStatus() const 
  55         case MIG_BAD_ARGUMENTS
: 
  57         case MIG_REMOTE_ERROR
: 
  58                 return CSSMERR_CSSM_SERVICE_NOT_AVAILABLE
;              // IPC mismatch of some sort 
  60                 return -1;              //@@@ some "internal error" code, perhaps? 
  64 int Error::unixError() const 
  67         case MIG_BAD_ARGUMENTS
: 
  69         case MIG_REMOTE_ERROR
: 
  70                 return ERPCMISMATCH
;            // IPC mismatch of some sort 
  72                 return -1;              //@@@ some "internal error" code, perhaps? 
  76 void Error::check(kern_return_t status
) 
  78         if (status 
!= KERN_SUCCESS
) 
  79                 Error::throwMe(status
); 
  82 void Error::throwMe(kern_return_t err
) 
  91 void *allocate(size_t size
) 
  94         check(vm_allocate(mach_task_self(), &address
, size
, true)); 
  95         return reinterpret_cast<void *>(address
); 
  98 void deallocate(vm_address_t address
, size_t size
) 
 100         check(vm_deallocate(mach_task_self(), address
, size
)); 
 107 mach_port_urefs_t 
Port::getRefs(mach_port_right_t right
) 
 109         mach_port_urefs_t count
; 
 110         check(::mach_port_get_refs(self(), mPort
, right
, &count
)); 
 114 mach_port_t 
Port::requestNotify(mach_port_t notify
, mach_msg_id_t type
, mach_port_mscount_t sync
) 
 116     mach_port_t previous
; 
 117     check(mach_port_request_notification(self(), mPort
, type
, sync
, notify
, 
 118         MACH_MSG_TYPE_MAKE_SEND_ONCE
, &previous
)); 
 121         const char *typeName
; 
 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; 
 130         if (notify 
== MACH_PORT_NULL
) 
 131                 secinfo("port", "%d cancel notify %s", port(), typeName
); 
 133                 secinfo("port", "%d request notify %s to %d (sync %d)", port(), typeName
, notify
, sync
); 
 139 mach_port_t 
Port::cancelNotify(mach_msg_id_t type
) 
 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... 
 144         return MACH_PORT_NULL
; 
 145         return requestNotify(MACH_PORT_NULL
, type
); 
 148 mach_port_msgcount_t 
Port::qlimit() const 
 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
; 
 158 void Port::qlimit(mach_port_msgcount_t limit
) 
 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
)); 
 170 set
<Port
> PortSet::members() const 
 172         mach_port_array_t members
; 
 173         mach_msg_type_number_t count
; 
 174         check(::mach_port_get_set_status(self(), mPort
, &members
, &count
)); 
 177                 copy(members
, members
+count
, inserter(result
, result
.begin())); 
 178                 vm_deallocate(self(), vm_address_t(members
), count 
* sizeof(members
[0])); 
 181                 vm_deallocate(self(), vm_address_t(members
), count 
* sizeof(members
[0])); 
 187 bool PortSet::contains(Port member
) const 
 189         set
<Port
> memberSet 
= members(); 
 190         return memberSet
.find(member
) != memberSet
.end(); 
 195 // Task port features 
 197 TaskPort::TaskPort(pid_t pid
) 
 199         check(::task_for_pid(self(), pid
, &mPort
)); 
 202 pid_t 
TaskPort::pid() const 
 205     check(::pid_for_task(mPort
, &pid
)); 
 211 // Bootstrap port management 
 213 mach_port_t 
Bootstrap::checkIn(const char *name
) const 
 216         check(::bootstrap_check_in(mPort
, makeName(name
), &port
)); 
 220 mach_port_t 
Bootstrap::checkInOptional(const char *name
) const 
 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
; 
 234 void Bootstrap::registerAs(mach_port_t port
, const char *name
) const 
 236         secinfo("bootstrap", "creating service port %d in %d:%s", port
, this->port(), name
); 
 237         check(::bootstrap_register(mPort
, makeName(name
), port
)); 
 240 mach_port_t 
Bootstrap::lookup(const char *name
) const 
 243         check(::bootstrap_look_up(mPort
, makeName(name
), &port
)); 
 247 mach_port_t 
Bootstrap::lookup2(const char *name
) const 
 250         check(::bootstrap_look_up2(mPort
, makeName(name
), &port
, 0, BOOTSTRAP_PRIVILEGED_SERVER
)); 
 254 mach_port_t 
Bootstrap::lookupOptional(const char *name
) const 
 257         kern_return_t err 
= ::bootstrap_look_up(mPort
, makeName(name
), &port
); 
 258     if (err 
== BOOTSTRAP_UNKNOWN_SERVICE
) 
 265 Bootstrap 
Bootstrap::subset(Port requestor
) 
 268     check(::bootstrap_subset(mPort
, requestor
, &sub
)); 
 276 ReceivePort::ReceivePort(const char *name
, const Bootstrap 
&bootstrap
, bool tryCheckin 
/* = true */) 
 279                 mPort 
= bootstrap
.checkInOptional(name
); 
 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); 
 293 // Stack-based bootstrap switcher 
 295 ModuleNexus
<Mutex
> StBootstrap::critical
; 
 297 StBootstrap::StBootstrap(const Bootstrap 
&newBoot
, const TaskPort 
&task
)  
 298     : mTask(task
), locker(critical()) 
 300     mOldBoot 
= Bootstrap(); 
 301     mTask
.bootstrap(newBoot
); 
 302     secinfo("StBoot", "bootstrap for %d switched to %d", mTask
.port(), newBoot
.port()); 
 305 StBootstrap::~StBootstrap() 
 307     mTask
.bootstrap(mOldBoot
); 
 308     secinfo("StBoot", "bootstrap for %d returned to %d", mTask
.port(), mOldBoot
.port()); 
 313 // Mach message buffers 
 315 Message::Message(void *buffer
, mach_msg_size_t size
) 
 316         : mBuffer(NULL
), mRelease(false) 
 318         setBuffer(buffer
, size
); 
 321 Message::Message(mach_msg_size_t size
) 
 322         : mBuffer(NULL
), mRelease(false) 
 328         : mBuffer(NULL
), mRelease(false) 
 338 void Message::setBuffer(void *buffer
, mach_msg_size_t size
) 
 341         mBuffer 
= reinterpret_cast<mig_reply_error_t 
*>(buffer
); 
 346 void Message::setBuffer(mach_msg_size_t size
) 
 348         assert(size 
>= sizeof(mach_msg_header_t
)); 
 350         mSize 
= size 
+ MAX_TRAILER_SIZE
; 
 351         mBuffer 
= reinterpret_cast<mig_reply_error_t 
*>(new char[mSize
]); 
 356 void Message::release() 
 359                 delete[] reinterpret_cast<char *>(mBuffer
); 
 363 bool Message::check(kern_return_t status
) 
 368     case MACH_RCV_TIMED_OUT
: 
 369     case MACH_SEND_TIMED_OUT
: 
 372         Error::throwMe(status
); 
 377 bool Message::send(mach_msg_option_t options
, 
 378     mach_msg_timeout_t timeout
, 
 379     mach_port_name_t notify
) 
 381     return check(mach_msg_overwrite(*this, 
 382         options 
| MACH_SEND_MSG
, 
 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
) 
 394     return check(mach_msg_overwrite(*this, 
 395         options 
| MACH_RCV_MSG
, 
 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
) 
 407     return check(mach_msg_overwrite(*this, 
 408         options 
| MACH_SEND_MSG 
| MACH_RCV_MSG
, 
 417 // Debug dumping of ports etc. 
 419 #if defined(DEBUGDUMP) 
 421 void Port::dump(const char *descr
) 
 423     if (mPort 
== MACH_PORT_NULL
) { 
 424         Debug::dump("[%s==NULL]\n", descr 
? descr 
: "port"); 
 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
)); 
 431             if (type 
& MACH_PORT_TYPE_SEND
) 
 432                 Debug::dump(" send(%d)", getRefs(MACH_PORT_RIGHT_SEND
)); 
 433             if (type 
& MACH_PORT_TYPE_RECEIVE
) 
 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
) 
 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
); 
 456 } // end namespace MachPlusPlus 
 457 } // end namespace Security