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