/* * Copyright (c) 2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "LKDCHelper-main.h" #include "LKDCHelper.h" /* MIG Generated files */ #include "LKDCHelperMessage.h" #include "LKDCHelperMessageServer.h" union max_msg_size { union __RequestUnion__request_LKDCHelper_subsystem request; union __ReplyUnion__request_LKDCHelper_subsystem reply; }; static const mach_msg_size_t MAX_MSG_SIZE = sizeof(union max_msg_size) + MAX_TRAILER_SIZE; static aslclient logclient = NULL; static int opt_debug; static struct timeval last_message; static pthread_t idletimer_thread; volatile int LKDCLogLevel = ASL_LEVEL_DEBUG; unsigned long maxidle = 0; static uid_t LKDCHelperUID = -1; static gid_t LKDCHelperGID = -1; static void helplogv(int level, const char *fmt, va_list ap) { if (NULL == logclient) { vfprintf(stderr, fmt, ap); fflush(stderr); } else { asl_vlog(logclient, NULL, level, fmt, ap); } } void helplog(int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); helplogv(level, fmt, ap); va_end(ap); } int authorized(audit_token_t *token) { int ok = 0; pid_t pid = (pid_t)-1; uid_t euid = (uid_t)-1; audit_token_to_au32(*token, NULL, &euid, NULL, NULL, NULL, &pid, NULL, NULL); ok = (euid == LKDCHelperUID || euid == 0); if (!ok) { helplog(ASL_LEVEL_NOTICE, "Unauthorized access by euid=%lu pid=%lu", (unsigned long)euid, (unsigned long)pid); } return ok; } static void initialize_logging(void) { logclient = asl_open(NULL, NULL, (opt_debug ? ASL_OPT_STDERR : 0)); if (NULL == logclient) { fprintf(stderr, "Could not initialize ASL logging.\n"); fflush(stderr); return; } if (opt_debug) { asl_set_filter(logclient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); } } void update_idle_timer(void) { assert(0 == gettimeofday(&last_message, NULL)); } static void * idletimer_main (void *context) { static struct timeval now; for (;;) { assert(0 == gettimeofday(&now, NULL)); if (now.tv_sec - last_message.tv_sec > (long) maxidle) { (void) request_LKDCHelperExit (*(mach_port_t*)context); sleep(1); } else { int t = maxidle - (now.tv_sec - last_message.tv_sec); if (t < 1) t = 1; sleep(t); } } return NULL; } static void initialize_timer(mach_port_t port) { int err; update_idle_timer(); err = pthread_create(&idletimer_thread, NULL, idletimer_main, &port); if (0 != err) { helplog(ASL_LEVEL_ERR, "Failed to start idletimer thread: %s", strerror(err)); } } static mach_port_t checkin(char *service_name) { kern_return_t kr = KERN_SUCCESS; mach_port_t port = MACH_PORT_NULL; launch_data_t msg, reply = NULL, datum = NULL; msg = launch_data_new_string (LAUNCH_KEY_CHECKIN); if (NULL == msg) { helplog(ASL_LEVEL_ERR, "Could not create checkin message for launchd."); goto fin; } reply = launch_msg (msg); if (NULL == reply) { helplog(ASL_LEVEL_ERR, "Could not message launchd."); goto fin; } if (LAUNCH_DATA_ERRNO == launch_data_get_type (reply)) { helplog(ASL_LEVEL_ERR, "Launchd checkin failed: %s.", strerror (launch_data_get_errno (reply)) ); goto fin; } datum = launch_data_dict_lookup (reply, LAUNCH_JOBKEY_MACHSERVICES); if (NULL == datum || LAUNCH_DATA_DICTIONARY != launch_data_get_type (datum)) { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES); goto fin; } datum = launch_data_dict_lookup (datum, service_name); if (NULL == datum || LAUNCH_DATA_MACHPORT != launch_data_get_type (datum)) { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s Mach port.", service_name); goto fin; } port = launch_data_get_machport (datum); if (MACH_PORT_NULL == port) { helplog(ASL_LEVEL_ERR, "Launchd gave me a null Mach port."); goto fin; } kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); if (KERN_SUCCESS != kr) { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %s", mach_error_string(kr)); goto fin; } fin: if (NULL != msg) { launch_data_free(msg); } if (NULL != reply) { launch_data_free(reply); } if (MACH_PORT_NULL == port) { exit(EXIT_FAILURE); } return port; } static mach_port_t register_service (const char *service_name) { mach_port_t port = MACH_PORT_NULL; kern_return_t kr; kr = mach_port_allocate (mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); if (KERN_SUCCESS != kr) { helplog(ASL_LEVEL_ERR, "mach_port_allocate: %s", mach_error_string(kr)); goto error; } kr = mach_port_insert_right (mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); if (KERN_SUCCESS != kr) { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %s", mach_error_string (kr)); goto error; } /* bootstrap_register2 does not modify its second argument, but the prototype does not include const. */ kr = bootstrap_register2 (bootstrap_port, (char *)service_name, port, 0); if (KERN_SUCCESS != kr) { helplog(ASL_LEVEL_ERR, "bootstrap_register2 failed: %s", mach_error_string (kr)); goto error; } return port; error: if (MACH_PORT_NULL != port) { mach_port_deallocate (mach_task_self (), port); } return MACH_PORT_NULL; } int main(int ac, char *av[]) { char *p = NULL; kern_return_t kr = KERN_FAILURE; mach_port_t port = MACH_PORT_NULL; long n; int ch; while ((ch = getopt(ac, av, "dt:")) != -1) switch (ch) { case 'd': opt_debug = 1; LKDCLogLevel = ASL_LEVEL_NOTICE; break; case 't': n = strtol(optarg, &p, 0); if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 1) { fprintf(stderr, "Invalid idle timeout: %s\n", optarg); exit(EXIT_FAILURE); } maxidle = n; break; case '?': default: fprintf(stderr, "Usage: [-d] [-t maxidle]\n"); exit(EXIT_FAILURE); } ac -= optind; av += optind; initialize_logging (); LKDCHelperUID = getuid (); LKDCHelperGID = getgid (); helplog (ASL_LEVEL_NOTICE, "Starting (uid=%lu)", (unsigned long)LKDCHelperUID); if (opt_debug) { port = register_service (kLKDCHelperName); } else { port = checkin (kLKDCHelperName); } if (maxidle > 0) { initialize_timer(port); } kr = mach_msg_server(LKDCHelper_server, MAX_MSG_SIZE, port, MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) ); if (KERN_SUCCESS != kr) { helplog(ASL_LEVEL_ERR, "mach_msg_server: %s\n", mach_error_string(kr)); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }