1/* 2 * Copyright (c) 2014 Apple Inc. 3 * All rights reserved. 4 */ 5#include <syslog.h> 6#include <sys/ioctl.h> 7#include <net/if.h> 8#include <netinet/in.h> 9#include <sys/socket.h> 10#include <arpa/inet.h> 11#include <mach/mach_time.h> 12#include <netinet/in_var.h> 13#include <xpc/private.h> 14#include <SystemConfiguration/SystemConfiguration.h> 15#include <SystemConfiguration/SNHelperPrivate.h> 16 17#include "ne_sm_bridge_private.h" 18 19#include "scnc_main.h" 20#include "scnc_client.h" 21#include "ipsec_manager.h" 22#include "ppp_manager.h" 23 24extern TAILQ_HEAD(, service) service_head; /* Defined in scnc_main.c */ 25extern CFBundleRef gBundleRef; /* Defined in scnc_main.c */ 26 27struct _ne_sm_bridge { 28 ne_sm_bridge_type_t type; 29 struct service serv; 30 void *info; 31 void (^disposable_callback)(void); 32}; 33 34static struct ne_sm_bridge_callbacks *g_callbacks = NULL; 35 36static void 37ne_sm_bridge_log(int level, CFStringRef format, ...) 38{ 39 va_list args; 40 va_start(args, format); 41 ne_sm_bridge_logv(level, format, args); 42 va_end(args); 43} 44 45bool 46ne_sm_bridge_logv(int level, CFStringRef format, va_list args) 47{ 48 if (g_callbacks != NULL && g_callbacks->log != NULL) { 49 g_callbacks->log(level, format, args); 50 return true; 51 } else { 52 return false; 53 } 54} 55 56bool 57ne_sm_bridge_is_logging_at_level(int level) 58{ 59 if (g_callbacks != NULL && g_callbacks->is_logging_at_level != NULL) { 60 return g_callbacks->is_logging_at_level(level); 61 } else { 62 return true; 63 } 64} 65 66CFDictionaryRef 67ne_sm_bridge_copy_configuration(ne_sm_bridge_t bridge) 68{ 69 if (g_callbacks != NULL && g_callbacks->copy_service_configuration != NULL) { 70 return g_callbacks->copy_service_configuration(bridge->info); 71 } else { 72 return NULL; 73 } 74} 75 76void 77ne_sm_bridge_status_changed(ne_sm_bridge_t bridge) 78{ 79 if (g_callbacks != NULL && g_callbacks->status_changed != NULL) { 80 g_callbacks->status_changed(bridge->info, scnc_getstatus(&bridge->serv)); 81 } 82} 83 84void 85ne_sm_bridge_acknowledge_sleep(ne_sm_bridge_t bridge) 86{ 87 if (g_callbacks != NULL && g_callbacks->acknowledge_sleep != NULL) { 88 g_callbacks->acknowledge_sleep(bridge->info); 89 } 90} 91 92void 93ne_sm_bridge_filter_state_dictionaries(ne_sm_bridge_t bridge, CFMutableArrayRef names, CFMutableArrayRef dictionaries) 94{ 95 if (g_callbacks != NULL && g_callbacks->filter_state_dictionaries != NULL) { 96 g_callbacks->filter_state_dictionaries(bridge->info, names, dictionaries); 97 } 98} 99 100CFStringRef 101ne_sm_bridge_copy_password_from_keychain(ne_sm_bridge_t bridge, CFStringRef type) 102{ 103 if (g_callbacks != NULL && g_callbacks->copy_password_from_keychain != NULL) { 104 return g_callbacks->copy_password_from_keychain(bridge->info, type); 105 } 106 return NULL; 107} 108 109void 110ne_sm_bridge_allow_dispose(ne_sm_bridge_t bridge) 111{ 112 if (bridge->disposable_callback != NULL) { 113 bridge->disposable_callback(); 114 Block_release(bridge->disposable_callback); 115 bridge->disposable_callback = NULL; 116 } 117} 118 119uint64_t 120ne_sm_bridge_get_connect_time(ne_sm_bridge_t bridge) 121{ 122 if (g_callbacks != NULL && g_callbacks->get_connect_time != NULL) { 123 return g_callbacks->get_connect_time(bridge->info); 124 } 125 return 0; 126} 127 128bool 129ne_sm_bridge_request_install(ne_sm_bridge_t bridge, bool exclusive) 130{ 131 if (g_callbacks != NULL && g_callbacks->request_install != NULL) { 132 g_callbacks->request_install(bridge->info, exclusive); 133 return true; 134 } 135 return false; 136} 137 138bool 139ne_sm_bridge_request_uninstall(ne_sm_bridge_t bridge) 140{ 141 if (g_callbacks != NULL && g_callbacks->request_uninstall != NULL) { 142 g_callbacks->request_uninstall(bridge->info); 143 return true; 144 } 145 return false; 146} 147 148bool 149ne_sm_bridge_start_profile_janitor(ne_sm_bridge_t bridge, CFStringRef profileIdentifier) 150{ 151 char profileIdentifierStr[256]; 152 if (profileIdentifier == NULL || !CFStringGetCString(profileIdentifier, profileIdentifierStr, sizeof(profileIdentifierStr), kCFStringEncodingUTF8)) { 153 return false; 154 } 155 156 if (g_callbacks != NULL && g_callbacks->start_profile_janitor != NULL) { 157 g_callbacks->start_profile_janitor(bridge->info, profileIdentifierStr); 158 return true; 159 } 160 return false; 161} 162 163void 164ne_sm_bridge_clear_saved_password(ne_sm_bridge_t bridge, CFStringRef type) 165{ 166#if NE_SM_BRIDGE_VERSION > 2 167 if (g_callbacks != NULL && g_callbacks->clear_saved_password != NULL) { 168 g_callbacks->clear_saved_password(bridge->info, type); 169 } 170#else 171#pragma unused(bridge, type) 172#endif 173} 174 175static bool 176init_controller(void) 177{ 178 static dispatch_once_t controller_once = 0; 179 static bool success = false; 180 181 dispatch_once(&controller_once, 182 ^{ 183 mach_timebase_info_data_t timebaseInfo; 184 const CFStringRef scdOptionsKeys[] = { kSCDynamicStoreUseSessionKeys }; 185 const CFBooleanRef scdOptionsValues[] = { kCFBooleanTrue }; 186 CFDictionaryRef scdOptions = NULL; 187 188 scnc_init_resources(gBundleRef); 189 190 /* Initialize time scale */ 191 if (mach_timebase_info(&timebaseInfo) != KERN_SUCCESS) { 192 ne_sm_bridge_log(LOG_ERR, CFSTR("init_controller: mach_timebase_info failed")); 193 goto fail; 194 } 195 gTimeScaleSeconds = ((double) timebaseInfo.numer / (double) timebaseInfo.denom) / 1000000000; 196 197 scdOptions = CFDictionaryCreate(kCFAllocatorDefault, 198 (const void **)scdOptionsKeys, 199 (const void **)scdOptionsValues, 200 sizeof(scdOptionsValues) / sizeof(scdOptionsValues[0]), 201 &kCFTypeDictionaryKeyCallBacks, 202 &kCFTypeDictionaryValueCallBacks); 203 204 gDynamicStore = SCDynamicStoreCreateWithOptions(kCFAllocatorDefault, CFSTR("NE - SCNC bridge"), scdOptions, NULL, NULL); 205 if (gDynamicStore == NULL) { 206 ne_sm_bridge_log(LOG_ERR, CFSTR("init_controller: SCDynamicStoreCreateWithOptions failed: %s"), SCErrorString(SCError())); 207 goto fail; 208 } 209 210 TAILQ_INIT(&service_head); 211 client_init_all(); 212 ipsec_init_things(); 213 214 success = true; 215fail: 216 if (scdOptions != NULL) { 217 CFRelease(scdOptions); 218 } 219 }); 220 221 return success; 222} 223 224static void 225bridge_destroy(ne_sm_bridge_t bridge) 226{ 227 if (bridge->type == NESMBridgeTypeIPSec) { 228 ipsec_dispose_service(&bridge->serv); 229 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 230 ppp_dispose_service(&bridge->serv); 231 } 232 233 CFRelease(bridge->serv.serviceID); 234 CFRelease(bridge->serv.typeRef); 235 if (bridge->serv.subtypeRef != NULL) { 236 CFRelease(bridge->serv.subtypeRef); 237 } 238 TAILQ_REMOVE(&service_head, &bridge->serv, next); 239 240 if (bridge->disposable_callback != NULL) { 241 Block_release(bridge->disposable_callback); 242 } 243 244 free(bridge->serv.sid); 245 free(bridge); 246} 247 248static ne_sm_bridge_t 249bridge_create(ne_sm_bridge_type_t type, CFStringRef serviceID, void *info) 250{ 251 ne_sm_bridge_t new_bridge; 252 CFIndex sid_len; 253 int error = 0; 254 255 if (!init_controller()) { 256 return NULL; 257 } 258 259 new_bridge = (ne_sm_bridge_t)malloc(sizeof(*new_bridge)); 260 memset(new_bridge, 0, sizeof(*new_bridge)); 261 262 new_bridge->type = type; 263 new_bridge->info = info; 264 265 new_bridge->serv.serviceID = CFRetain(serviceID); 266 sid_len = CFStringGetLength(serviceID) + 1; 267 new_bridge->serv.sid = malloc(sid_len); 268 CFStringGetCString(serviceID, (char*)new_bridge->serv.sid, sid_len, kCFStringEncodingUTF8); 269 270 new_bridge->serv.ne_sm_bridge = new_bridge; 271 272 if (type == NESMBridgeTypeIPSec) { 273 new_bridge->serv.typeRef = CFRetain(kSCValNetInterfaceTypeIPSec); 274 new_bridge->serv.type = TYPE_IPSEC; 275 ipsec_new_service(&new_bridge->serv); 276 error = ipsec_setup_service(&new_bridge->serv); 277 if (error) { 278 ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: ipsec_setup_service failed: %d"), error); 279 } 280 } else if (type == NESMBridgeTypeL2TP || type == NESMBridgeTypePPTP) { 281 new_bridge->serv.typeRef = CFRetain(kSCValNetInterfaceTypePPP); 282 new_bridge->serv.type = TYPE_PPP; 283 if (type == NESMBridgeTypeL2TP) { 284 new_bridge->serv.subtypeRef = CFRetain(kSCValNetInterfaceSubTypeL2TP); 285 } else { 286 new_bridge->serv.subtypeRef = CFRetain(kSCValNetInterfaceSubTypePPTP); 287 } 288 new_bridge->serv.subtype = ppp_subtype(new_bridge->serv.subtypeRef); 289 ppp_new_service(&new_bridge->serv); 290 error = ppp_setup_service(&new_bridge->serv); 291 if (error) { 292 ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: ppp_setup_service failed: %d"), error); 293 } 294 } 295 296 new_bridge->serv.unit = findfreeunit(new_bridge->serv.type, new_bridge->serv.subtype); 297 if (new_bridge->serv.unit == 0xFFFF) { 298 ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: findfreeunit failed")); 299 error = ENOMEM; 300 } 301 302 TAILQ_INSERT_TAIL(&service_head, &new_bridge->serv, next); 303 304 if (error) { 305 bridge_destroy(new_bridge); 306 return NULL; 307 } 308 309 new_bridge->serv.initialized = TRUE; 310 311 return new_bridge; 312} 313 314static void 315bridge_handle_network_change_event(ne_sm_bridge_t bridge, const char *ifname, nwi_ifstate_flags flags) 316{ 317 if (bridge->type == NESMBridgeTypeIPSec && scnc_getstatus(&bridge->serv) != kSCNetworkConnectionDisconnected) { 318 struct { 319 struct kern_event_msg msg; 320 uint8_t buffer[sizeof(struct kev_in_data)]; 321 } event; 322 struct kev_in_data *inetdata = (struct kev_in_data *)event.msg.event_data; 323 int unitIdx; 324 325 memset(&event, 0, sizeof(event)); 326 327 if (flags & NWI_IFSTATE_FLAGS_HAS_IPV4) { 328 int ifr_socket; 329 struct ifreq ifr; 330 int ioctl_result; 331 332 memset(&ifr, 0, sizeof(ifr)); 333 ifr.ifr_addr.sa_family = AF_INET; 334 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1); 335 336 ifr_socket = socket(AF_INET, SOCK_DGRAM, 0); 337 ioctl_result = ioctl(ifr_socket, SIOCGIFADDR, &ifr); 338 close(ifr_socket); 339 340 if (ioctl_result < 0) { 341 ne_sm_bridge_log(LOG_ERR, CFSTR("ioctl(SIOCGIFADDR) failed: %s"), strerror(errno)); 342 return; 343 } 344 345 memcpy(&inetdata->ia_addr, &((struct sockaddr_in *)(void *)&ifr.ifr_addr)->sin_addr, sizeof(inetdata->ia_addr)); 346 } 347 348 for (unitIdx = 0; ifname[unitIdx] != '\0' && !isdigit(ifname[unitIdx]); unitIdx++); 349 350 strncpy(inetdata->link_data.if_name, ifname, unitIdx); 351 352 if (isdigit(ifname[unitIdx])) { 353 inetdata->link_data.if_unit = (uint32_t)strtol((ifname + unitIdx), NULL, 10); 354 } 355 356 if (!strcmp(inetdata->link_data.if_name, "ppp")) { 357 inetdata->link_data.if_family = APPLE_IF_FAM_PPP; 358 } 359 360 if (flags & NWI_IFSTATE_FLAGS_HAS_IPV4) { 361 event.msg.event_code = KEV_INET_NEW_ADDR; 362 } else { 363 event.msg.event_code = KEV_INET_ADDR_DELETED; 364 } 365 366 if (ne_sm_bridge_is_logging_at_level(LOG_DEBUG)) { 367 if (event.msg.event_code == KEV_INET_NEW_ADDR) { 368 char addr_str[INET_ADDRSTRLEN]; 369 memset(addr_str, 0, sizeof(addr_str)); 370 inet_ntop(AF_INET, &inetdata->ia_addr, addr_str, sizeof(addr_str)); 371 372 ne_sm_bridge_log(LOG_DEBUG, CFSTR("Network change event: added address %s to interface %s%d (family %d)"), addr_str, inetdata->link_data.if_name, inetdata->link_data.if_unit, inetdata->link_data.if_family); 373 } else if (event.msg.event_code == KEV_INET_ADDR_DELETED) { 374 ne_sm_bridge_log(LOG_DEBUG, CFSTR("Network change event: deleted address from interface %s%d (family %d)"), inetdata->link_data.if_name, inetdata->link_data.if_unit, inetdata->link_data.if_family); 375 } 376 } 377 378 ipsec_network_event(&bridge->serv, &event.msg); 379 } 380} 381 382static bool 383bridge_handle_sleep(ne_sm_bridge_t bridge) 384{ 385 bool result = false; 386 387 if (bridge->type == NESMBridgeTypeIPSec) { 388 result = ipsec_will_sleep(&bridge->serv, 0); 389 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 390 result = (ppp_will_sleep(&bridge->serv, 0) > 0); 391 } 392 393 ne_sm_bridge_log(LOG_DEBUG, CFSTR("handle sleep for bridge type %d returning %d"), bridge->type, result); 394 395 return result; 396} 397 398static bool 399bridge_can_sleep(ne_sm_bridge_t bridge) 400{ 401 bool result = true; 402 403 if (bridge->type == NESMBridgeTypeIPSec) { 404 result = ipsec_can_sleep(&bridge->serv); 405 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 406 result = ppp_can_sleep(&bridge->serv); 407 } 408 409 ne_sm_bridge_log(LOG_DEBUG, CFSTR("can sleep for bridge type %d returning %d"), bridge->type, result); 410 411 return result; 412} 413 414static void 415bridge_handle_sleep_time(ne_sm_bridge_t bridge, double sleep_time) 416{ 417 ne_sm_bridge_log(LOG_INFO, CFSTR("System slept for %f secs"), sleep_time); 418 if (bridge->serv.flags & FLAG_SETUP_DISCONNECTONWAKE) { 419 double wake_timeout = 0; 420#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR 421 wake_timeout = scnc_getsleepwaketimeout(&bridge->serv); 422#endif 423 424 ne_sm_bridge_log(LOG_INFO, CFSTR("Session is configured to disconnect on wake if slept for more than %f seconds"), wake_timeout); 425 bridge->serv.connectionslepttime += (uint32_t)sleep_time; 426 427 if (sleep_time > wake_timeout) { 428 scnc_idle_disconnect(&bridge->serv); 429 } 430 } 431} 432 433static void 434bridge_handle_wakeup(ne_sm_bridge_t bridge) 435{ 436 ne_sm_bridge_log(LOG_INFO, CFSTR("Handling wake up for bridge type %d"), bridge->type); 437 if (bridge->type == NESMBridgeTypeIPSec) { 438 ipsec_wake_up(&bridge->serv); 439 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 440 ppp_wake_up(&bridge->serv); 441 } 442} 443 444static bool 445bridge_handle_start(ne_sm_bridge_t bridge, CFDictionaryRef options, uid_t uid, gid_t gid, mach_port_t bootstrap_port, mach_port_t audit_session_port, bool on_demand) 446{ 447 if (bridge->type == NESMBridgeTypeIPSec) { 448 return (ipsec_start(&bridge->serv, options, uid, gid, bootstrap_port, false, on_demand) == 0); 449 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 450 return (ppp_start(&bridge->serv, options, uid, gid, bootstrap_port, audit_session_port, false, on_demand) == 0); 451 } 452 return false; 453} 454 455static bool 456bridge_handle_stop(ne_sm_bridge_t bridge) 457{ 458 ne_sm_bridge_log(LOG_INFO, CFSTR("Handling stop for bridge type %d"), bridge->type); 459 if (bridge->type == NESMBridgeTypeIPSec) { 460 return (ipsec_stop(&bridge->serv, 0) == 0); 461 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 462 return (ppp_stop(&bridge->serv, SIGTERM) == 0); 463 } 464 return false; 465} 466 467static void 468bridge_get_security_session_info(ne_sm_bridge_t bridge, xpc_object_t request, mach_port_t *bootstrap_port, mach_port_t *audit_session_port) 469{ 470 xpc_connection_t connection = xpc_dictionary_get_remote_connection(request); 471 xpc_object_t entitlement = xpc_connection_copy_entitlement_value(connection, NESessionManagerPrivilegedEntitlement); 472 473 if ((bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) && 474 isa_xpc_bool(entitlement) && xpc_bool_get_value(entitlement)) 475 { 476 *bootstrap_port = bridge->serv.bootstrap; 477 *audit_session_port = bridge->serv.au_session; 478 } else { 479 *bootstrap_port = MACH_PORT_NULL; 480 *audit_session_port = MACH_PORT_NULL; 481 } 482 483 if (entitlement) { 484 xpc_release(entitlement); 485 } 486} 487 488static CFDictionaryRef 489bridge_copy_configuration(ne_sm_bridge_t bridge, xpc_object_t request) 490{ 491 xpc_connection_t connection = xpc_dictionary_get_remote_connection(request); 492 CFDictionaryRef serviceConfig = ne_sm_bridge_copy_configuration(bridge); 493 CFDictionaryRef userOptions = NULL; 494 CFMutableDictionaryRef info = NULL; 495 int error = 0; 496 497 if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 498 xpc_object_t entitlement = xpc_connection_copy_entitlement_value(connection, NESessionManagerPrivilegedEntitlement); 499 500 if (isa_xpc_bool(entitlement) && xpc_bool_get_value(entitlement)) 501 { 502 error = ppp_getconnectdata(&bridge->serv, &userOptions, true); 503 } else { 504 error = ppp_getconnectdata(&bridge->serv, &userOptions, false); 505 } 506 507 if (entitlement) { 508 xpc_release(entitlement); 509 } 510 } else if (bridge->type == NESMBridgeTypeIPSec) { 511 error = ipsec_getconnectdata(&bridge->serv, &userOptions, false); 512 } 513 514 if (error == 0 && userOptions != NULL) { 515 if (info == NULL) { 516 info = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 517 } 518 CFDictionarySetValue(info, CFSTR(NESMSessionLegacyUserConfigurationKey), userOptions); 519 } 520 521 if (userOptions != NULL) { 522 CFRelease(userOptions); 523 } 524 525 if (serviceConfig != NULL) { 526 if (info == NULL) { 527 info = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 528 } 529 CFDictionarySetValue(info, CFSTR(NESMSessionLegacyServiceConfigurationKey), serviceConfig); 530 CFRelease(serviceConfig); 531 } 532 533 return info; 534} 535 536static void 537bridge_set_disposable_callback(ne_sm_bridge_t bridge, void (^callback)(void)) 538{ 539 if (bridge->disposable_callback != NULL) { 540 Block_release(bridge->disposable_callback); 541 } 542 bridge->disposable_callback = Block_copy(callback); 543} 544 545static CFDictionaryRef 546bridge_copy_statistics(ne_sm_bridge_t bridge) 547{ 548 int error = 0; 549 CFDictionaryRef statsdict = NULL; 550 551 if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 552 error = ppp_copystatistics(&bridge->serv, &statsdict); 553 } else if (bridge->type == NESMBridgeTypeIPSec) { 554 error = ipsec_copystatistics(&bridge->serv, &statsdict); 555 } 556 557 if (error) { 558 ne_sm_bridge_log(LOG_NOTICE, CFSTR("Failed to copy statistics: %s"), strerror(error)); 559 } 560 561 return statsdict; 562} 563 564static CFDictionaryRef 565bridge_copy_extended_status(ne_sm_bridge_t bridge) 566{ 567 int error = 0; 568 CFDictionaryRef statusdict = NULL; 569 570 if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 571 error = ppp_copyextendedstatus(&bridge->serv, &statusdict); 572 } else if (bridge->type == NESMBridgeTypeIPSec) { 573 error = ipsec_copyextendedstatus(&bridge->serv, &statusdict); 574 } 575 576 if (error) { 577 ne_sm_bridge_log(LOG_NOTICE, CFSTR("Failed to copy extended status: %s"), strerror(error)); 578 } 579 580 return statusdict; 581} 582 583static void 584bridge_install(ne_sm_bridge_t bridge) 585{ 586 int error = 0; 587 588 if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 589 error = ppp_install(&bridge->serv); 590 } else if (bridge->type == NESMBridgeTypeIPSec) { 591 error = ipsec_install(&bridge->serv); 592 } 593 594 if (error) { 595 ne_sm_bridge_log(LOG_NOTICE, CFSTR("Failed to install: %s"), strerror(error)); 596 } 597} 598 599static void 600bridge_uninstall(ne_sm_bridge_t bridge) 601{ 602 int error = 0; 603 604 if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 605 error = ppp_uninstall(&bridge->serv); 606 } else if (bridge->type == NESMBridgeTypeIPSec) { 607 error = ipsec_uninstall(&bridge->serv); 608 } 609 610 if (error) { 611 ne_sm_bridge_log(LOG_NOTICE, CFSTR("Failed to uninstall: %s"), strerror(error)); 612 } 613} 614 615static void 616bridge_handle_configuration_change(ne_sm_bridge_t bridge) 617{ 618 int error; 619 620 if (bridge->type == NESMBridgeTypeIPSec) { 621 error = ipsec_setup_service(&bridge->serv); 622 if (error) { 623 ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: ipsec_setup_service failed: %d"), error); 624 } 625 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 626 error = ppp_setup_service(&bridge->serv); 627 if (error) { 628 ne_sm_bridge_log(LOG_ERR, CFSTR("bridge_create: ppp_setup_service failed: %d"), error); 629 } 630 } 631} 632 633static void 634bridge_handle_user_logout(ne_sm_bridge_t bridge) 635{ 636 if (bridge->type == NESMBridgeTypeIPSec) { 637 ipsec_log_out(&bridge->serv); 638 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 639 ppp_log_out(&bridge->serv); 640 } 641} 642 643static void 644bridge_handle_user_switch(ne_sm_bridge_t bridge) 645{ 646 if (bridge->type == NESMBridgeTypeIPSec) { 647 ipsec_log_switch(&bridge->serv); 648 } else if (bridge->type == NESMBridgeTypeL2TP || bridge->type == NESMBridgeTypePPTP) { 649 ppp_log_switch(&bridge->serv); 650 } 651} 652 653static void 654bridge_handle_device_lock(ne_sm_bridge_t bridge) 655{ 656 if (bridge->type == NESMBridgeTypeIPSec) { 657 ipsec_device_lock(&bridge->serv); 658 } 659} 660 661static void 662bridge_handle_device_unlock(ne_sm_bridge_t bridge) 663{ 664 if (bridge->type == NESMBridgeTypeIPSec) { 665 ipsec_device_unlock(&bridge->serv); 666 } 667} 668 669extern ne_sm_bridge_functions_t 670ne_sm_bridge_copy_functions(struct ne_sm_bridge_callbacks *callbacks, CFBundleRef bundle) 671{ 672 static dispatch_once_t copy_functions_once = 0; 673 static ne_sm_bridge_functions_t functions = NULL; 674 675 dispatch_once(©_functions_once, 676 ^{ 677 functions = (ne_sm_bridge_functions_t)malloc(sizeof(*functions)); 678 679 memset(functions, 0, sizeof(*functions)); 680 681 functions->create = bridge_create; 682 functions->destroy = bridge_destroy; 683 functions->handle_network_change_event = bridge_handle_network_change_event; 684 functions->handle_sleep = bridge_handle_sleep; 685 functions->can_sleep = bridge_can_sleep; 686 functions->handle_sleep_time = bridge_handle_sleep_time; 687 functions->handle_wakeup = bridge_handle_wakeup; 688 functions->handle_start = bridge_handle_start; 689 functions->handle_stop = bridge_handle_stop; 690 functions->get_security_session_info = bridge_get_security_session_info; 691 functions->set_disposable_callback = bridge_set_disposable_callback; 692 functions->copy_statistics = bridge_copy_statistics; 693 functions->copy_extended_status = bridge_copy_extended_status; 694 functions->copy_configuration = bridge_copy_configuration; 695 functions->install = bridge_install; 696 functions->uninstall = bridge_uninstall; 697 functions->handle_configuration_change = bridge_handle_configuration_change; 698 functions->handle_user_logout = bridge_handle_user_logout; 699 functions->handle_user_switch = bridge_handle_user_switch; 700 functions->handle_device_lock = bridge_handle_device_lock; 701 functions->handle_device_unlock = bridge_handle_device_unlock; 702 703 g_callbacks = (struct ne_sm_bridge_callbacks *)malloc(sizeof(*g_callbacks)); 704 memcpy(g_callbacks, callbacks, sizeof(*g_callbacks)); 705 gBundleRef = (CFBundleRef)CFRetain(bundle); 706 }); 707 708 return functions; 709} 710 711