1/*
2 * Copyright (c) 2007 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 <CoreFoundation/CoreFoundation.h>
25#include <OpenDirectory/OpenDirectory.h>
26
27#include <sys/cdefs.h>
28#include <sys/time.h>
29#include <sys/types.h>
30#include <bsm/libbsm.h>
31#include <mach/mach.h>
32#include <mach/mach_error.h>
33#include <bootstrap_priv.h>
34#include <launch.h>
35#include <pwd.h>
36#include <dirent.h>
37#include <pthread.h>
38#include <stdarg.h>
39#include <stdbool.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <time.h>
43#include <unistd.h>
44#include <limits.h>
45
46#include "LKDCHelper-main.h"
47#include "LKDCHelper.h"
48#include "utils.h"
49
50/* MIG Generated files */
51#include "LKDCHelperMessage.h"
52#include "LKDCHelperMessageServer.h"
53
54union max_msg_size {
55		union __RequestUnion__request_LKDCHelper_subsystem	request;
56		union __ReplyUnion__request_LKDCHelper_subsystem	reply;
57};
58
59static const mach_msg_size_t MAX_MSG_SIZE = sizeof(union max_msg_size) + MAX_TRAILER_SIZE;
60static aslclient logclient = NULL;
61static int opt_debug;
62static struct timeval last_message;
63static pthread_t idletimer_thread;
64
65char *LocalLKDCRealm = NULL;
66
67volatile int	LKDCLogLevel = ASL_LEVEL_DEBUG;
68
69unsigned long maxidle = 600;
70
71static uid_t LKDCHelperUID = -1;
72static gid_t LKDCHelperGID = -1;
73
74static void
75helplogv(int level, const char *fmt, va_list ap)
76{
77	if (NULL == logclient) {
78		vfprintf(stderr, fmt, ap);
79		fflush(stderr);
80	} else {
81		asl_vlog(logclient, NULL, level, fmt, ap);
82	}
83}
84
85void
86helplog(int level, const char *fmt, ...)
87{
88	va_list ap;
89
90	va_start(ap, fmt);
91	helplogv(level, fmt, ap);
92	va_end(ap);
93}
94
95int
96authorized(audit_token_t token)
97{
98	int ok = 0;
99	pid_t pid = (pid_t)-1;
100	uid_t euid = (uid_t)-1;
101
102	audit_token_to_au32(token, NULL, &euid, NULL, NULL, NULL, &pid, NULL, NULL);
103
104	ok = (euid == LKDCHelperUID || euid == 0);
105	if (!ok) {
106		helplog(ASL_LEVEL_NOTICE, "Unauthorized access by euid=%lu pid=%lu",
107				(unsigned long)euid, (unsigned long)pid);
108	}
109	return ok;
110}
111
112static void
113initialize_logging(void)
114{
115	logclient = asl_open(NULL, NULL, (opt_debug ? ASL_OPT_STDERR : 0));
116	if (NULL == logclient) {
117		fprintf(stderr, "Could not initialize ASL logging.\n");
118		fflush(stderr);
119		return;
120	}
121	if (opt_debug) {
122		asl_set_filter(logclient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
123	}
124}
125
126void
127update_idle_timer(void)
128{
129	gettimeofday(&last_message, NULL);
130}
131
132static void *
133idletimer_main (void *context)
134{
135	struct timeval now;
136
137	for (;;) {
138		gettimeofday(&now, NULL);
139		if (now.tv_sec - last_message.tv_sec > (long) maxidle) {
140			exit(1);
141		} else {
142			int t = maxidle - (now.tv_sec - last_message.tv_sec);
143			if (t < 1)
144				t = 1;
145			sleep(t + 11); /* sleep past idle exit */
146		}
147	}
148
149	return NULL;
150}
151
152static void
153initialize_timer(void)
154{
155	int err;
156
157	update_idle_timer();
158
159	err = pthread_create(&idletimer_thread, NULL, idletimer_main, NULL);
160
161	if (0 != err) {
162		helplog(ASL_LEVEL_ERR, "Failed to start idletimer thread: %s", strerror(err));
163	}
164}
165
166static mach_port_t
167checkin(char *service_name)
168{
169	kern_return_t kr = KERN_SUCCESS;
170	mach_port_t port = MACH_PORT_NULL;
171	launch_data_t msg, reply = NULL, datum = NULL;
172
173	msg = launch_data_new_string (LAUNCH_KEY_CHECKIN);
174	if (NULL == msg) {
175		helplog(ASL_LEVEL_ERR, "Could not create checkin message for launchd.");
176		goto fin;
177	}
178
179	reply = launch_msg (msg);
180	if (NULL == reply) {
181		helplog(ASL_LEVEL_ERR, "Could not message launchd.");
182		goto fin;
183	}
184
185	if (LAUNCH_DATA_ERRNO == launch_data_get_type (reply)) {
186		helplog(ASL_LEVEL_ERR, "Launchd checkin failed: %s.",
187			strerror (launch_data_get_errno (reply))
188		       );
189		goto fin;
190	}
191
192	datum = launch_data_dict_lookup (reply, LAUNCH_JOBKEY_MACHSERVICES);
193	if (NULL == datum || LAUNCH_DATA_DICTIONARY != launch_data_get_type (datum)) {
194		helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES);
195		goto fin;
196	}
197
198	datum = launch_data_dict_lookup (datum, service_name);
199	if (NULL == datum || LAUNCH_DATA_MACHPORT != launch_data_get_type (datum)) {
200		helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s Mach port.", service_name);
201		goto fin;
202	}
203
204	port = launch_data_get_machport (datum);
205	if (MACH_PORT_NULL == port) {
206		helplog(ASL_LEVEL_ERR, "Launchd gave me a null Mach port.");
207		goto fin;
208	}
209
210	kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
211	if (KERN_SUCCESS != kr) {
212		helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %s", mach_error_string(kr));
213		goto fin;
214	}
215
216fin:
217	if (NULL != msg)   { launch_data_free(msg); }
218	if (NULL != reply) { launch_data_free(reply); }
219	if (MACH_PORT_NULL == port) { exit(EXIT_FAILURE); }
220	return port;
221}
222
223static mach_port_t
224register_service (const char *service_name)
225{
226	mach_port_t port = MACH_PORT_NULL;
227	kern_return_t kr;
228
229	kr = mach_port_allocate (mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
230
231	if (KERN_SUCCESS != kr) {
232		helplog(ASL_LEVEL_ERR, "mach_port_allocate: %s", mach_error_string(kr));
233		goto error;
234	}
235
236	kr = mach_port_insert_right (mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
237	if (KERN_SUCCESS != kr) {
238		helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %s", mach_error_string (kr));
239		goto error;
240	}
241
242	/* bootstrap_register2 does not modify its second argument, but the prototype does not include const. */
243	kr = bootstrap_register2 (bootstrap_port, (char *)service_name, port, 0);
244	if (KERN_SUCCESS != kr) {
245		helplog(ASL_LEVEL_ERR, "bootstrap_register2 failed: %s", mach_error_string (kr));
246		goto error;
247	}
248	return port;
249error:
250	if (MACH_PORT_NULL != port) { mach_port_deallocate (mach_task_self (), port); }
251	return MACH_PORT_NULL;
252}
253
254static void
255GetLocalLKDCRealm(void)
256{
257	CFArrayRef inKDCAttributes = NULL;
258	ODNodeRef localRef = NULL;
259	ODRecordRef kdcConfRef = NULL;
260	CFArrayRef data = NULL;
261	CFTypeRef attrs[]	= { CFSTR("dsAttrTypeStandard:RealName") };
262
263	inKDCAttributes = CFArrayCreate(NULL, attrs, 1, &kCFTypeArrayCallBacks);
264	if (inKDCAttributes == NULL)
265		goto out;
266
267	localRef = ODNodeCreateWithName(kCFAllocatorDefault,
268					kODSessionDefault,
269					CFSTR("/Local/Default"),
270					NULL);
271 	if (localRef == NULL)
272		goto out;
273
274	kdcConfRef = ODNodeCopyRecord(localRef, kODRecordTypeConfiguration,
275				      CFSTR("KerberosKDC"),
276				      inKDCAttributes,
277				      NULL);
278	if (kdcConfRef == NULL)
279		goto out;
280
281	data = ODRecordCopyValues(kdcConfRef,
282				  CFSTR("dsAttrTypeStandard:RealName"),
283				  NULL);
284	if (data == NULL)
285		goto out;
286
287	if (CFArrayGetCount(data) != 1)
288		goto out;
289
290	__KRBCreateUTF8StringFromCFString((CFStringRef)CFArrayGetValueAtIndex(data, 0),
291					  &LocalLKDCRealm);
292
293    out:
294	if (localRef)
295	    CFRelease(localRef);
296	if (kdcConfRef)
297	    CFRelease(kdcConfRef);
298	if (data)
299	    CFRelease(data);
300	if (inKDCAttributes)
301		CFRelease(inKDCAttributes);
302}
303
304
305
306int
307main(int ac, char *av[])
308{
309	char *p = NULL;
310	kern_return_t kr = KERN_FAILURE;
311	mach_port_t port = MACH_PORT_NULL;
312	long n;
313	int ch;
314
315	while ((ch = getopt(ac, av, "dt:")) != -1)
316		switch (ch) {
317		case 'd':
318			opt_debug = 1;
319			LKDCLogLevel = ASL_LEVEL_NOTICE;
320			break;
321		case 't':
322			n = strtol(optarg, &p, 0);
323			if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 1) {
324				fprintf(stderr, "Invalid idle timeout: %s\n", optarg);
325				exit(EXIT_FAILURE);
326			}
327			maxidle = n;
328			break;
329		case '?':
330		default:
331			fprintf(stderr, "Usage: [-d] [-t maxidle]\n");
332			exit(EXIT_FAILURE);
333		}
334
335	initialize_logging ();
336
337	LKDCHelperUID = getuid ();
338	LKDCHelperGID = getgid ();
339
340	GetLocalLKDCRealm();
341
342	helplog (ASL_LEVEL_NOTICE, "Starting (uid=%lu)", (unsigned long)LKDCHelperUID);
343
344	if (opt_debug) {
345		port = register_service (kLKDCHelperName);
346	} else {
347		port = checkin (kLKDCHelperName);
348	}
349
350	if (maxidle > 0) {
351		initialize_timer();
352	}
353
354	kr = mach_msg_server(LKDCHelper_server, MAX_MSG_SIZE, port,
355				MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) |
356				MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)
357			    );
358
359	if (KERN_SUCCESS != kr) {
360		helplog(ASL_LEVEL_ERR, "mach_msg_server: %s\n", mach_error_string(kr));
361		exit(EXIT_FAILURE);
362	}
363	exit(EXIT_SUCCESS);
364}
365
366