1/* 2 * Copyright (c) 2010-2014 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 25/* 26 * eapolcfg_auth.c 27 * - launch-on-demand daemon running as root to perform privileged operations 28 * required by certain EAPOLClientConfiguration.h APIs 29 * - uses AuthorizationRef for validating that caller has permission to 30 * perform the operation 31 */ 32 33/* 34 * Modification History 35 * 36 * February 22, 2010 Dieter Siegmund (dieter@apple.com) 37 * - initial revision 38 */ 39#include <stdlib.h> 40#include <unistd.h> 41#include <bsm/libbsm.h> 42#include <sys/types.h> 43#include <sysexits.h> 44#include <servers/bootstrap.h> 45 46#include <CoreFoundation/CFRunLoop.h> 47#include <CoreFoundation/CFRuntime.h> 48#include <SystemConfiguration/SystemConfiguration.h> 49#include <SystemConfiguration/SCPrivate.h> 50#include <SystemConfiguration/SCValidation.h> 51#include "EAPOLClientConfiguration.h" 52#include "EAPOLClientConfigurationPrivate.h" 53#include "EAPCertificateUtil.h" 54#include "symbol_scope.h" 55#include "eapolcfg_authServer.h" 56#include "eapolcfg_auth_types.h" 57#include "myCFUtil.h" 58 59STATIC Boolean S_handled_request; 60 61INLINE void 62my_vm_deallocate(vm_address_t data, int data_length) 63{ 64 if (data != 0) { 65 (void)vm_deallocate(mach_task_self(), data, data_length); 66 } 67 return; 68} 69 70STATIC Boolean 71authorization_is_valid(const void * auth_data, int auth_data_length) 72{ 73 AuthorizationExternalForm * auth_ext_p; 74 AuthorizationRef authorization; 75 AuthorizationFlags flags; 76 AuthorizationItem item; 77 AuthorizationRights rights; 78 OSStatus status; 79 80 if (auth_data == NULL 81 || auth_data_length != sizeof(auth_ext_p->bytes)) { 82 syslog(LOG_ERR, "eapolcfg_auth: authorization NULL/invalid size"); 83 return (FALSE); 84 } 85 auth_ext_p = (AuthorizationExternalForm *)auth_data; 86 status = AuthorizationCreateFromExternalForm(auth_ext_p, &authorization); 87 if (status != errAuthorizationSuccess) { 88 syslog(LOG_ERR, "eapolcfg_auth: authorization is invalid (%d)", 89 (int)status); 90 return (FALSE); 91 } 92 rights.count = 1; 93 rights.items = &item; 94 item.name = "system.preferences"; 95 item.value = NULL; 96 item.valueLength = 0; 97 item.flags = 0; 98 flags = kAuthorizationFlagDefaults; 99 flags |= kAuthorizationFlagExtendRights; 100 flags |= kAuthorizationFlagInteractionAllowed; 101 status = AuthorizationCopyRights(authorization, 102 &rights, 103 kAuthorizationEmptyEnvironment, 104 flags, 105 NULL); 106 AuthorizationFree(authorization, kAuthorizationFlagDefaults); 107 return (status == errAuthorizationSuccess); 108} 109 110STATIC int 111init_itemID_and_cfg(xmlData_t itemID_data, 112 mach_msg_type_number_t itemID_data_length, 113 EAPOLClientItemIDRef * itemID_p, 114 EAPOLClientConfigurationRef * cfg_p) 115{ 116 EAPOLClientConfigurationRef cfg = NULL; 117 EAPOLClientItemIDRef itemID = NULL; 118 CFDictionaryRef itemID_dict = NULL; 119 int ret = 0; 120 121 itemID_dict 122 = my_CFPropertyListCreateWithBytePtrAndLength(itemID_data, 123 itemID_data_length); 124 if (isA_CFDictionary(itemID_dict) == NULL) { 125 ret = EINVAL; 126 goto done; 127 } 128 cfg = EAPOLClientConfigurationCreate(NULL); 129 if (cfg == NULL) { 130 ret = ENOMEM; 131 goto done; 132 } 133 itemID = EAPOLClientItemIDCreateWithDictionary(cfg, itemID_dict); 134 if (itemID == NULL) { 135 ret = EINVAL; 136 goto done; 137 } 138 done: 139 my_CFRelease(&itemID_dict); 140 if (ret == 0) { 141 *cfg_p = cfg; 142 *itemID_p = itemID; 143 } 144 else { 145 my_CFRelease(&itemID); 146 my_CFRelease(&cfg); 147 } 148 return (ret); 149} 150 151/** 152 ** MiG server routines 153 **/ 154PRIVATE_EXTERN kern_return_t 155eapolclientitemid_set_identity(mach_port_t server, 156 OOBData_t auth_data, 157 mach_msg_type_number_t auth_data_length, 158 xmlData_t itemID_data, 159 mach_msg_type_number_t itemID_data_length, 160 OOBData_t id_handle, 161 mach_msg_type_number_t id_handle_length, 162 int * result) 163{ 164 EAPOLClientConfigurationRef cfg = NULL; 165 SecIdentityRef identity = NULL; 166 EAPOLClientItemIDRef itemID = NULL; 167 int ret = 0; 168 169 if (authorization_is_valid(auth_data, auth_data_length) == FALSE) { 170 ret = EPERM; 171 goto done; 172 } 173 ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg); 174 if (ret != 0) { 175 goto done; 176 } 177 if (id_handle != NULL) { 178 CFDataRef data; 179 OSStatus status; 180 181 data = CFDataCreateWithBytesNoCopy(NULL, 182 (const UInt8 *)id_handle, 183 id_handle_length, 184 kCFAllocatorNull); 185 if (data == NULL) { 186 ret = EINVAL; 187 goto done; 188 } 189 status = EAPSecIdentityHandleCreateSecIdentity(data, &identity); 190 CFRelease(data); 191 if (status != noErr) { 192 ret = ENOENT; 193 goto done; 194 } 195 } 196 if (EAPOLClientItemIDSetIdentity(itemID, 197 kEAPOLClientDomainSystem, 198 identity) == FALSE) { 199 ret = ENXIO; 200 } 201 202 done: 203 my_vm_deallocate((vm_address_t)auth_data, auth_data_length); 204 my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length); 205 my_vm_deallocate((vm_address_t)id_handle, id_handle_length); 206 my_CFRelease(&cfg); 207 my_CFRelease(&itemID); 208 my_CFRelease(&identity); 209 *result = ret; 210 return (KERN_SUCCESS); 211} 212 213kern_return_t 214eapolclientitemid_set_password(mach_port_t server, 215 OOBData_t auth_data, 216 mach_msg_type_number_t auth_data_length, 217 xmlData_t itemID_data, 218 mach_msg_type_number_t itemID_data_length, 219 uint32_t flags, 220 OOBData_t name, 221 mach_msg_type_number_t name_length, 222 OOBData_t password, 223 mach_msg_type_number_t password_length, 224 int * result) 225{ 226 EAPOLClientConfigurationRef cfg = NULL; 227 EAPOLClientItemIDRef itemID = NULL; 228 CFDataRef name_data = NULL; 229 CFDataRef password_data = NULL; 230 int ret = 0; 231 232 if (authorization_is_valid(auth_data, auth_data_length) == FALSE) { 233 ret = EPERM; 234 goto done; 235 } 236 ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg); 237 if (ret != 0) { 238 goto done; 239 } 240 if ((flags & keapolcfg_auth_set_name) != 0) { 241 name_data = CFDataCreate(NULL, (const UInt8 *)name, 242 name_length); 243 } 244 else { 245 syslog(LOG_NOTICE, "not setting name"); 246 } 247 if ((flags & keapolcfg_auth_set_password) != 0) { 248 password_data = CFDataCreate(NULL, (const UInt8 *)password, 249 password_length); 250 } 251 else { 252 syslog(LOG_NOTICE, "not setting password"); 253 } 254 if (EAPOLClientItemIDSetPasswordItem(itemID, 255 kEAPOLClientDomainSystem, 256 name_data, password_data) == FALSE) { 257 ret = ENXIO; 258 } 259 260 done: 261 my_vm_deallocate((vm_address_t)auth_data, auth_data_length); 262 my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length); 263 my_vm_deallocate((vm_address_t)name, name_length); 264 my_vm_deallocate((vm_address_t)password, password_length); 265 my_CFRelease(&name_data); 266 my_CFRelease(&password_data); 267 my_CFRelease(&cfg); 268 my_CFRelease(&itemID); 269 *result = ret; 270 return (KERN_SUCCESS); 271} 272 273PRIVATE_EXTERN kern_return_t 274eapolclientitemid_remove_password(mach_port_t server, 275 OOBData_t auth_data, 276 mach_msg_type_number_t auth_data_length, 277 xmlData_t itemID_data, 278 mach_msg_type_number_t itemID_data_length, 279 int * result) 280{ 281 EAPOLClientConfigurationRef cfg = NULL; 282 SecIdentityRef identity = NULL; 283 EAPOLClientItemIDRef itemID = NULL; 284 CFDataRef name_data = NULL; 285 CFDataRef password_data = NULL; 286 int ret = 0; 287 288 if (authorization_is_valid(auth_data, auth_data_length) == FALSE) { 289 ret = EPERM; 290 goto done; 291 } 292 ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg); 293 if (ret != 0) { 294 goto done; 295 } 296 if (EAPOLClientItemIDRemovePasswordItem(itemID, kEAPOLClientDomainSystem) 297 == FALSE) { 298 ret = ENXIO; 299 } 300 301 done: 302 my_vm_deallocate((vm_address_t)auth_data, auth_data_length); 303 my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length); 304 my_CFRelease(&cfg); 305 my_CFRelease(&itemID); 306 *result = ret; 307 return (KERN_SUCCESS); 308} 309 310PRIVATE_EXTERN kern_return_t 311eapolclientitemid_check_password(mach_port_t server, 312 OOBData_t auth_data, 313 mach_msg_type_number_t auth_data_length, 314 xmlData_t itemID_data, 315 mach_msg_type_number_t itemID_data_length, 316 OOBDataOut_t * name, 317 mach_msg_type_number_t * name_length, 318 boolean_t * password_set, 319 int * result) 320{ 321 EAPOLClientConfigurationRef cfg = NULL; 322 SecIdentityRef identity = NULL; 323 EAPOLClientItemIDRef itemID = NULL; 324 CFDataRef name_data = NULL; 325 vm_address_t name_vm_data = 0; 326 mach_msg_type_number_t name_vm_length = 0; 327 CFDataRef password_data = NULL; 328 vm_address_t password_vm_data = 0; 329 mach_msg_type_number_t password_vm_length = 0; 330 int ret = 0; 331 332 *password_set = FALSE; 333 if (authorization_is_valid(auth_data, auth_data_length) == FALSE) { 334 ret = EPERM; 335 goto done; 336 } 337 ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg); 338 if (ret != 0) { 339 goto done; 340 } 341 if (EAPOLClientItemIDCopyPasswordItem(itemID, kEAPOLClientDomainSystem, 342 &name_data, &password_data) 343 == FALSE) { 344 ret = ENOENT; 345 } 346 else { 347 kern_return_t kret; 348 349 if (name_data != NULL) { 350 name_vm_length = CFDataGetLength(name_data); 351 kret = vm_allocate(mach_task_self(), &name_vm_data, 352 name_vm_length, TRUE); 353 if (kret != KERN_SUCCESS) { 354 ret = ENOMEM; 355 goto done; 356 } 357 bcopy((char *)CFDataGetBytePtr(name_data), 358 (char *)(name_vm_data), name_vm_length); 359 CFRelease(name_data); 360 } 361 if (password_data != NULL) { 362 *password_set = TRUE; 363 CFRelease(password_data); 364 } 365 } 366 367 done: 368 if (ret != 0) { 369 if (name_vm_data != 0) { 370 (void)vm_deallocate(mach_task_self(), name_vm_data, 371 name_vm_length); 372 name_vm_data = 0; 373 name_vm_length = 0; 374 } 375 } 376 *name = (char *)name_vm_data; 377 *name_length = name_vm_length; 378 my_vm_deallocate((vm_address_t)auth_data, auth_data_length); 379 my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length); 380 my_CFRelease(&cfg); 381 my_CFRelease(&itemID); 382 *result = ret; 383 return (KERN_SUCCESS); 384} 385 386 387/** 388 ** server message handling 389 **/ 390 391STATIC boolean_t 392process_notification(mach_msg_header_t * request) 393{ 394 mach_no_senders_notification_t * notify; 395 396 notify = (mach_no_senders_notification_t *)request; 397 if (notify->not_header.msgh_id > MACH_NOTIFY_LAST 398 || notify->not_header.msgh_id < MACH_NOTIFY_FIRST) { 399 return FALSE; /* if this is not a notification message */ 400 } 401 return (TRUE); 402} 403 404STATIC void 405server_handle_request(CFMachPortRef port, void * msg, CFIndex size, void * info) 406{ 407 mach_msg_return_t r; 408 mach_msg_header_t * request = (mach_msg_header_t *)msg; 409 mach_msg_header_t * reply; 410 char reply_s[eapolcfg_auth_subsystem.maxsize]; 411 412 if (process_notification(request) == FALSE) { 413 reply = (mach_msg_header_t *)reply_s; 414 if (eapolcfg_auth_server(request, reply) == FALSE) { 415 syslog(LOG_NOTICE, 416 "eapolcfg_auth: unknown message ID (%d)", 417 request->msgh_id); 418 mach_msg_destroy(request); 419 } 420 else { 421 int options; 422 423 S_handled_request = TRUE; 424 425 options = MACH_SEND_MSG; 426 if (MACH_MSGH_BITS_REMOTE(reply->msgh_bits) 427 != MACH_MSG_TYPE_MOVE_SEND_ONCE) { 428 options |= MACH_SEND_TIMEOUT; 429 } 430 r = mach_msg(reply, 431 options, 432 reply->msgh_size, 433 0, 434 MACH_PORT_NULL, 435 MACH_MSG_TIMEOUT_NONE, 436 MACH_PORT_NULL); 437 if (r != MACH_MSG_SUCCESS) { 438 syslog(LOG_NOTICE, "eapolcfg_auth: mach_msg(send): %s", 439 mach_error_string(r)); 440 mach_msg_destroy(reply); 441 } 442 } 443 } 444 return; 445} 446 447static void 448start_service(mach_port_t service_port) 449{ 450 CFMachPortRef mp; 451 CFRunLoopSourceRef rls; 452 453 mp = CFMachPortCreateWithPort(NULL, service_port, server_handle_request, 454 NULL, NULL); 455 rls = CFMachPortCreateRunLoopSource(NULL, mp, 0); 456 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 457 CFRelease(mp); 458 CFRelease(rls); 459 return; 460} 461 462int 463main(int argc, char **argv) 464{ 465 kern_return_t kr; 466 mach_port_t service_port = MACH_PORT_NULL; 467 468 openlog("eapolcfg_auth", LOG_CONS | LOG_PID, LOG_DAEMON); 469 if (geteuid() != 0) { 470 syslog(LOG_ERR, "not running as root - exiting"); 471 exit(EX_CONFIG); 472 } 473 kr = bootstrap_check_in(bootstrap_port, EAPOLCFG_AUTH_SERVER, 474 &service_port); 475 if (kr != BOOTSTRAP_SUCCESS) { 476 syslog(LOG_ERR, "bootstrap_check_in() failed: %s", 477 bootstrap_strerror(kr)); 478 exit(EX_UNAVAILABLE); 479 } 480 start_service(service_port); 481 while (1) { 482 SInt32 rlStatus; 483 484 rlStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 15.0, TRUE); 485 if (rlStatus == kCFRunLoopRunTimedOut) { 486 if (S_handled_request == FALSE) { 487 /* we didn't handle a request in the last time interval */ 488 break; 489 } 490 S_handled_request = FALSE; 491 } 492 } 493 exit(EX_OK); 494 return (0); 495} 496