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