1/* 2 * Copyright (c) 2005-2008, 2010, 2011, 2013 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#include <fcntl.h> 25#include <paths.h> 26#include <signal.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <unistd.h> 31#include <sys/param.h> 32#include <sys/types.h> 33#include <sys/socket.h> 34#include <sys/un.h> 35#include <sys/wait.h> 36#include <mach/mach.h> 37#include <mach/mach_error.h> 38#include <servers/bootstrap.h> 39#include <bootstrap_priv.h> 40#include <pthread.h> 41 42#include <CoreFoundation/CoreFoundation.h> 43#include <SystemConfiguration/SCPrivate.h> 44 45#include "SCHelper_client.h" 46#include "helper.h" // MiG generated file 47 48 49#define HELPER "SCHelper" 50#define HELPER_LEN (sizeof(HELPER) - 1) 51 52#define SUFFIX_SYM "~sym" 53#define SUFFIX_SYM_LEN (sizeof(SUFFIX_SYM) - 1) 54 55 56static pthread_mutex_t _helper_lock = PTHREAD_MUTEX_INITIALIZER; 57static mach_port_t _helper_server = MACH_PORT_NULL; 58 59 60static mach_port_t 61__SCHelperServerPort(kern_return_t *status) 62{ 63 mach_port_t server = MACH_PORT_NULL; 64 char *server_name; 65 66 server_name = getenv("SCHELPER_SERVER"); 67 if (!server_name) { 68 server_name = SCHELPER_SERVER; 69 } 70 71#ifdef BOOTSTRAP_PRIVILEGED_SERVER 72 *status = bootstrap_look_up2(bootstrap_port, 73 server_name, 74 &server, 75 0, 76 BOOTSTRAP_PRIVILEGED_SERVER); 77#else // BOOTSTRAP_PRIVILEGED_SERVER 78 *status = bootstrap_look_up(bootstrap_port, server_name, &server); 79#endif // BOOTSTRAP_PRIVILEGED_SERVER 80 81 switch (*status) { 82 case BOOTSTRAP_SUCCESS : 83 /* service currently registered, "a good thing" (tm) */ 84 return server; 85 case BOOTSTRAP_NOT_PRIVILEGED : 86 /* the service is not privileged */ 87 break; 88 case BOOTSTRAP_UNKNOWN_SERVICE : 89 /* service not currently registered, try again later */ 90 break; 91 default : 92#ifdef DEBUG 93 SCLog(_sc_verbose, LOG_DEBUG, 94 CFSTR("__SCHelperServerPort bootstrap_look_up() failed: status=%s"), 95 bootstrap_strerror(*status)); 96#endif /* DEBUG */ 97 break; 98 } 99 100 return MACH_PORT_NULL; 101} 102 103 104__private_extern__ 105Boolean 106_SCHelperOpen(CFDataRef authorizationData, mach_port_t *helper_port) 107{ 108 kern_return_t kr; 109 Boolean ok; 110 mach_port_t server; 111 uint32_t status = 0; 112 113 *helper_port = MACH_PORT_NULL; 114 115 // open a new session with the server 116 server = _helper_server; 117 while (TRUE) { 118 if (server != MACH_PORT_NULL) { 119 kr = helperinit(server, 120 helper_port, 121 &status); 122 if (kr == KERN_SUCCESS) { 123 break; 124 } 125 126 // our [cached] server port is not valid 127 if (kr != MACH_SEND_INVALID_DEST) { 128 // if we got an unexpected error, don't retry 129 status = kr; 130 break; 131 } 132 } 133 134 pthread_mutex_lock(&_helper_lock); 135 if (_helper_server != MACH_PORT_NULL) { 136 if (server == _helper_server) { 137 // if the server we tried returned the error 138 (void)mach_port_deallocate(mach_task_self(), _helper_server); 139 _helper_server = __SCHelperServerPort(&kr); 140 if (_helper_server == MACH_PORT_NULL) { 141 status = kr; 142 } 143 } else { 144 // another thread has refreshed the SCHelper server port 145 } 146 } else { 147 _helper_server = __SCHelperServerPort(&kr); 148 if (_helper_server == MACH_PORT_NULL) { 149 status = kr; 150 } 151 } 152 server = _helper_server; 153 pthread_mutex_unlock(&_helper_lock); 154 155 if (server == MACH_PORT_NULL) { 156 // if SCHelper server not available 157 break; 158 } 159 } 160 __MACH_PORT_DEBUG(TRUE, "*** _SCHelperOpen", *helper_port); 161 162 if (*helper_port == MACH_PORT_NULL) { 163 SCLog(TRUE, LOG_ERR, 164 CFSTR("_SCHelperOpen: could not contact server: %s"), 165 SCErrorString(status)); 166 return FALSE; 167 } 168 169 ok = _SCHelperExec(*helper_port, SCHELPER_MSG_AUTH, authorizationData, &status, NULL); 170 if (!ok) { 171 SCLog(TRUE, LOG_INFO, CFSTR("_SCHelperOpen: could not send authorization")); 172 goto error; 173 } 174 175 ok = (status == 0); 176 if (!ok) { 177 SCLog(TRUE, LOG_INFO, CFSTR("could not start \"" HELPER "\", status = %u"), status); 178 goto error; 179 } 180 181 return TRUE; 182 183 error : 184 185 if (*helper_port != MACH_PORT_NULL) { 186 (void)mach_port_deallocate(mach_task_self(), *helper_port); 187 *helper_port = MACH_PORT_NULL; 188 } 189 190 return FALSE; 191 192} 193 194 195__private_extern__ 196void 197_SCHelperClose(mach_port_t *helper_port) 198{ 199 if (!_SCHelperExec(*helper_port, SCHELPER_MSG_EXIT, NULL, NULL, NULL)) { 200 SCLog(TRUE, LOG_INFO, CFSTR("_SCHelperOpen: could not send exit request")); 201 } 202 203 if (*helper_port != MACH_PORT_NULL) { 204 (void)mach_port_deallocate(mach_task_self(), *helper_port); 205 *helper_port = MACH_PORT_NULL; 206 } 207 208 return; 209} 210 211 212static CFDataRef 213_SCHelperExecCopyBacktrace() 214{ 215 static Boolean loggingEnabled = FALSE; 216 static dispatch_once_t once; 217 CFDataRef traceData = NULL; 218 219 dispatch_once(&once, ^{ 220 if(getenv("ENABLE_SCHELPER_BACKTRACES")) { 221 loggingEnabled = TRUE; 222 } 223 }); 224 225 if (loggingEnabled) { 226 CFStringRef backtrace; 227 228 backtrace = _SC_copyBacktrace(); 229 if (backtrace != NULL) { 230 _SCSerializeString(backtrace, &traceData, NULL, NULL); 231 CFRelease(backtrace); 232 } 233 } 234 235 return traceData; 236} 237 238 239Boolean 240_SCHelperExec(mach_port_t port, uint32_t msgID, CFDataRef data, uint32_t *status, CFDataRef *reply) 241{ 242 kern_return_t kr; 243 CFDataRef myData = NULL; 244 xmlDataOut_t replyRef = NULL; /* raw bytes */ 245 mach_msg_type_number_t replyLen = 0; 246 uint32_t replyStatus = 0; 247 CFDataRef traceData; 248 249 traceData = _SCHelperExecCopyBacktrace(); 250 251 kr = helperexec(port, 252 msgID, 253 (data != NULL) ? (void *)CFDataGetBytePtr(data) : NULL, 254 (data != NULL) ? (mach_msg_type_number_t)CFDataGetLength(data) : 0, 255 (traceData != NULL) ? (void *)CFDataGetBytePtr(traceData) : NULL, 256 (traceData != NULL) ? (mach_msg_type_number_t)CFDataGetLength(traceData) : 0, 257 &replyStatus, 258 &replyRef, 259 &replyLen); 260 261 if (traceData != NULL) { 262 CFRelease(traceData); 263 } 264 265 if (kr != KERN_SUCCESS) { 266 if (replyRef != NULL) { 267 (void) vm_deallocate(mach_task_self(), (vm_address_t)replyRef, replyLen); 268 } 269 270 if (kr != MACH_SEND_INVALID_DEST) { 271 // if we got an unexpected error 272 SCLog(TRUE, LOG_ERR, CFSTR("_SCHelperExec() failed: %s"), mach_error_string(kr)); 273 } 274 _SCErrorSet(kr); 275 276 return FALSE; 277 } 278 279 // un-serialize the reply 280 if (replyRef != NULL) { 281 if (!_SCUnserializeData(&myData, replyRef, replyLen)) { 282 return FALSE; 283 } 284 } 285 286 if (status != NULL) { 287 *status = replyStatus; 288 } 289 290 if (reply != NULL) { 291 *reply = myData; 292 } else if (myData != NULL) { 293 SCLog(TRUE, LOG_DEBUG, CFSTR("_SCHelperExec() data available with no place to go")); 294 CFRelease(myData); 295 } 296 297 return TRUE; 298} 299