/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * 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@ */ /* ----------------------------------------------------------------------------- includes ----------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include // for SCLog() #define SCLog #include "ppp_msg.h" #include "ppp_privmsg.h" #include "../Family/ppp_domain.h" #include "../Helpers/pppd/pppd.h" #include "ppp_client.h" #include "ppp_command.h" #include "ppp_manager.h" #include "ppp_option.h" /* ----------------------------------------------------------------------------- Definitions ----------------------------------------------------------------------------- */ enum { READ = 0, // read end of standard UNIX pipe WRITE = 1 // write end of standard UNIX pipe }; /* ----------------------------------------------------------------------------- globals ----------------------------------------------------------------------------- */ TAILQ_HEAD(, ppp) ppp_head; io_connect_t gIOPort; io_connect_t gSleeping; long gSleepArgument; CFUserNotificationRef gSleepNotification; double gTimeScaleSeconds; /* ----------------------------------------------------------------------------- Forward declarations ----------------------------------------------------------------------------- */ static void ppp_display_error(struct ppp *ppp); static void ppp_sleep(void * x, io_service_t y, natural_t messageType, void *messageArgument); static void exec_callback(pid_t pid, int status, struct rusage *rusage, void *context); static void exec_postfork(pid_t pid, void *arg); u_int32_t ppp_translate_error(u_int16_t subtype, u_int32_t native_ppp_error, u_int32_t native_dev_error); int get_str (struct ppp *ppp, CFDictionaryRef optsdict, CFStringRef entity, CFStringRef property, u_char *opt, u_int32_t *outlen, CFDictionaryRef setupdict, u_char lookinsetup, u_char *defaultval); int ppp_addclient(struct ppp *ppp, void *client, int autoclose); int ppp_delclient(struct ppp *ppp, void *client); int ppp_delclients(struct ppp *ppp); struct ppp_client *ppp_getclient(struct ppp *ppp, void *client); /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_long ppp_init_all() { IONotificationPortRef notify; io_object_t iterator; mach_timebase_info_data_t timebaseInfo; gSleeping = 0; // obviously we are not sleeping :-) gSleepNotification = 0; TAILQ_INIT(&ppp_head); /* install the power management callback */ gIOPort = IORegisterForSystemPower(0, ¬ify, ppp_sleep, &iterator); if (gIOPort == 0) { printf("IORegisterForSystemPower failed\n"); return 1; } if (mach_timebase_info(&timebaseInfo) != KERN_SUCCESS) { // returns scale factor for ns printf("mach_timebase_info failed\n"); return 1; } gTimeScaleSeconds = ((double) timebaseInfo.numer / (double) timebaseInfo.denom) / 1000000000; CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notify), kCFRunLoopDefaultMode); /* read configuration from database */ if (options_init_all()) { return 1; } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_dispose_all() { // dispose ppp data structures } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_int32_t ppp_makeref(struct ppp *ppp) { return (((u_long)ppp->subtype) << 16) + ppp->unit; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_int32_t ppp_makeifref(struct ppp *ppp) { return (((u_long)ppp->subtype) << 16) + ppp->ifunit; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ struct ppp *ppp_findbyname(u_char *name, u_short ifunit) { struct ppp *ppp; TAILQ_FOREACH(ppp, &ppp_head, next) { if ((ppp->ifunit == ifunit) && !strncmp(ppp->name, name, IFNAMSIZ)) { return ppp; } } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ struct ppp *ppp_findbyserviceID(CFStringRef serviceID) { struct ppp *ppp; TAILQ_FOREACH(ppp, &ppp_head, next) if (CFEqual(ppp->serviceID, serviceID)) return ppp; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ struct ppp *ppp_findbysid(u_char *data, int len) { struct ppp *ppp; TAILQ_FOREACH(ppp, &ppp_head, next) if (ppp->sid && (strlen(ppp->sid) == len) && !strncmp(ppp->sid, data, len)) return ppp; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_setorder(struct ppp *ppp, u_int16_t order) { TAILQ_REMOVE(&ppp_head, ppp, next); switch (order) { case 0: TAILQ_INSERT_HEAD(&ppp_head, ppp, next); break; case 0xFFFF: TAILQ_INSERT_TAIL(&ppp_head, ppp, next); break; } } /* ----------------------------------------------------------------------------- find the ppp structure corresponding to the reference if ref == -1, then return the default structure (first in the list) if ref == ----------------------------------------------------------------------------- */ struct ppp *ppp_findbyref(u_long ref) { u_short subtype = ref >> 16; u_short unit = ref & 0xFFFF; struct ppp *ppp; TAILQ_FOREACH(ppp, &ppp_head, next) { if (((ppp->subtype == subtype) || (subtype == 0xFFFF)) && ((ppp->unit == unit) || (unit == 0xFFFF))) { return ppp; } } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_setphase(struct ppp *ppp, int phase) { ppp->phase = phase; client_notify(ppp->sid, ppp_makeref(ppp), ppp->phase, 0, 2); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_printlist() { struct ppp *ppp; SCLog(TRUE, LOG_INFO, CFSTR("Printing list of ppp services : \n")); TAILQ_FOREACH(ppp, &ppp_head, next) { SCLog(TRUE, LOG_INFO, CFSTR("Service : %@, subtype = %d\n"), ppp->serviceID, ppp->subtype); } } /* ----------------------------------------------------------------------------- find the ppp structure corresponding to the reference if ref == -1, then return the default structure (first in the list) if ref == ----------------------------------------------------------------------------- */ struct ppp *ppp_findbyifref(u_long ref) { u_short subtype = ref >> 16; u_short ifunit = ref & 0xFFFF; struct ppp *ppp; TAILQ_FOREACH(ppp, &ppp_head, next) { if (((ppp->subtype == subtype) || (subtype == 0xFFFF)) && ((ppp->ifunit == ifunit) || (ifunit == 0xFFFF))) { return ppp; } } return 0; } /* ----------------------------------------------------------------------------- get the first free ref numer within a family ----------------------------------------------------------------------------- */ u_short ppp_findfreeunit(u_short subtype) { struct ppp *ppp = TAILQ_FIRST(&ppp_head); u_short unit = 0; while (ppp) { if ((subtype == ppp->subtype) && (ppp->unit == unit)) { unit++; if (unit == 0xFFFF) return unit; ppp = TAILQ_FIRST(&ppp_head); // restart } else ppp = TAILQ_NEXT(ppp, next); // continue } return unit; } /* ----------------------------------------------------------------------------- an interface structure needs to be created unit is the ppp managed unit, not the ifunit ----------------------------------------------------------------------------- */ struct ppp *ppp_new(CFStringRef serviceID , CFStringRef subtypeRef) { struct ppp *ppp; u_short unit, subtype, len; CFURLRef url; u_char str[MAXPATHLEN], str2[32]; //SCLog(LOG_INFO, CFSTR("ppp_new, subtype = %%@, serviceID = %@\n"), subtypeRef, serviceID); if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPSerial)) subtype = PPP_TYPE_SERIAL; else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPoE)) subtype = PPP_TYPE_PPPoE; else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPTP)) subtype = PPP_TYPE_PPTP; else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypeL2TP)) subtype = PPP_TYPE_L2TP; // else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPoA)) // subtype = PPP_TYPE_PPPoA; // else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPISDN)) // subtype = PPP_TYPE_ISDN; // else if (CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPOther)) // subtype = PPP_TYPE_OTHER; else subtype = PPP_TYPE_OTHER; unit = ppp_findfreeunit(subtype); if (unit == 0xFFFF) return 0; // no room left... ppp = malloc(sizeof(struct ppp)); if (!ppp) return 0; // very bad... bzero(ppp, sizeof(struct ppp)); ppp->serviceID = CFRetain(serviceID); ppp->subtypeRef = CFRetain(subtypeRef); // keep a C version of the service ID len = CFStringGetLength(serviceID) + 1; if (ppp->sid = malloc(len)) { CFStringGetCString(serviceID, ppp->sid, len, kCFStringEncodingUTF8); } ppp->unit = unit; ppp->ifunit = 0xFFFF; // no real unit yet ppp->pid = -1; strncpy(ppp->name, "ppp", IFNAMSIZ); ppp->subtype = subtype; ppp_setphase(ppp, PPP_IDLE); ppp->alertenable = OPT_ALERT_DEF; strcpy(str, DIR_KEXT); str2[0] = 0; CFStringGetCString(ppp->subtypeRef, str2, sizeof(str2), kCFStringEncodingUTF8); strcat(str, str2); strcat(str, ".ppp"); // add plugin suffix url = CFURLCreateFromFileSystemRepresentation(NULL, str, strlen(str), TRUE); if (url) { ppp->bundle = CFBundleCreate(0, url); CFRelease(url); } TAILQ_INIT(&ppp->client_head); TAILQ_INSERT_TAIL(&ppp_head, ppp, next); return ppp; } /* ----------------------------------------------------------------------------- changed for this ppp occured in configd cache ----------------------------------------------------------------------------- */ void ppp_updatestate(struct ppp *ppp, CFDictionaryRef service) { u_int32_t lval; int val; u_char str[16]; struct ppp *ppp2; if (getNumber(service, kSCPropNetPPPStatus, &lval)) { // just started and status not yet published if (ppp->started_link == 0 || lval) { ppp->started_link = 0; ppp_setphase(ppp, lval); //CFStringGetCString(ppp->serviceID, str, sizeof(str), kCFStringEncodingUTF8); //printf("service '%s', state = %d\n", str, lval); ppp->ifunit = 0xFFFF; if (ppp->phase == PPP_RUNNING || ppp->phase == PPP_ONHOLD || ppp->phase == PPP_STATERESERVED) { // should get the interfce name at the right place if (getString(service, kSCPropInterfaceName, str, sizeof(str))) { sscanf(str, "ppp%d", &val); ppp->ifunit = val; } } } } if (ppp->kill_link) { //CFStringGetCString(ppp->serviceID, str, sizeof(str), kCFStringEncodingUTF8); //printf("update state, need kill link for service '%s'\n", str); val = ppp->kill_link; ppp->kill_link = 0; ppp_dodisconnect(ppp, val, 0); } if (ppp->needdispose && !ppp_dispose(ppp)) return; if (!getNumber(service, kSCPropNetPPPDeviceLastCause, &ppp->lastdevstatus)) ppp->lastdevstatus = 0; if (!getNumber(service, kSCPropNetPPPLastCause, &ppp->laststatus)) ppp->laststatus = 0; if (!getNumber(service, kSCPropNetPPPConnectTime, &ppp->conntime)) ppp->conntime = 0; if (!getNumber(service, kSCPropNetPPPDisconnectTime, &ppp->disconntime)) ppp->disconntime = 0; if (ppp->phase != ppp->oldphase) { switch (ppp->phase) { case PPP_STATERESERVED: case PPP_HOLDOFF: // forget about the last error when we rearm the dial on demand if (ppp->oldphase != PPP_INITIALIZE && ppp->oldphase != PPP_HOLDOFF) ppp_display_error(ppp); /* check if setup has changed */ if (ppp->setupchanged) ppp_dodisconnect(ppp, 15, 0); break; case PPP_IDLE: ppp->kill_sent = 0; if (ppp->oldphase != PPP_STATERESERVED && ppp->oldphase != PPP_HOLDOFF) ppp_display_error(ppp); if (gSleeping) { TAILQ_FOREACH(ppp2, &ppp_head, next) if (ppp2->phase != PPP_IDLE) break; if (ppp2 == 0) { if (gSleepNotification) { CFUserNotificationCancel(gSleepNotification); CFRelease(gSleepNotification); gSleepNotification = 0; } IOAllowPowerChange(gIOPort, gSleepArgument); } } if (ppp->connectopts) { CFRelease(ppp->connectopts); ppp->connectopts = 0; } if (ppp->needconnect) { ppp->needconnect = 0; ppp_doconnect(ppp, ppp->needconnectopts, 0, 0, 0); if (ppp->needconnectopts) CFRelease(ppp->needconnectopts); ppp->needconnectopts = 0; } else { // if pppd was started with dialondemand, and exited because of too many failure // then don't rearm dialondemand if ((ppp->setupchanged || ((ppp->dialondemand == 0) && (ppp->laststatus != EXIT_USER_REQUEST))) && (getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &lval) && lval) && (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &lval) || !lval || gLoggedInUser)) { ppp_doconnect(ppp, 0, 1, 0, 0); } } break; } ppp->oldphase = ppp->phase; } return; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ CFStringRef ppp_copyUserLocalizedString(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFArrayRef userLanguages) { CFStringRef result = NULL, errStr= NULL; CFDictionaryRef stringTable; CFDataRef tableData; SInt32 errCode; CFURLRef tableURL; CFArrayRef locArray, prefArray; if (userLanguages == NULL) return CFBundleCopyLocalizedString(bundle, key, value, NULL); if (key == NULL) return (value ? CFRetain(value) : CFRetain(CFSTR(""))); locArray = CFBundleCopyBundleLocalizations(bundle); if (locArray) { prefArray = CFBundleCopyLocalizationsForPreferences(locArray, userLanguages); if (prefArray) { if (CFArrayGetCount(prefArray)) { tableURL = CFBundleCopyResourceURLForLocalization(bundle, CFSTR("Localizable"), CFSTR("strings"), NULL, CFArrayGetValueAtIndex(prefArray, 0)); if (tableURL) { if (CFURLCreateDataAndPropertiesFromResource(NULL, tableURL, &tableData, NULL, NULL, &errCode)) { stringTable = CFPropertyListCreateFromXMLData(NULL, tableData, kCFPropertyListImmutable, &errStr); if (errStr) CFRelease(errStr); if (stringTable) { result = CFDictionaryGetValue(stringTable, key); if (result) CFRetain(result); CFRelease(stringTable); } CFRelease(tableData); } CFRelease(tableURL); } } CFRelease(prefArray); } CFRelease(locArray); } if (result == NULL) result = (value && !CFEqual(value, CFSTR(""))) ? CFRetain(value) : CFRetain(key); return result; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void ppp_display_error(struct ppp *ppp) { CFStringRef ppp_msg = NULL, dev_msg = NULL, msg = NULL; CFPropertyListRef langRef = NULL; if ((ppp->alertenable & PPP_ALERT_ERRORS) == 0) return; if (gLoggedInUser) { CFPreferencesSynchronize(kCFPreferencesAnyApplication, gLoggedInUser, kCFPreferencesAnyHost); langRef = CFPreferencesCopyValue(CFSTR("AppleLanguages"), kCFPreferencesAnyApplication, gLoggedInUser, kCFPreferencesAnyHost); } if (ppp->lastdevstatus && ppp->bundle) { dev_msg = CFStringCreateWithFormat(0, 0, CFSTR("Device Error %d"), ppp->lastdevstatus); if (dev_msg) msg = ppp_copyUserLocalizedString(ppp->bundle, dev_msg, dev_msg, langRef); } if (msg == NULL) { ppp_msg = CFStringCreateWithFormat(0, 0, CFSTR("PPP Error %d"), ppp->laststatus); if (ppp_msg) msg = ppp_copyUserLocalizedString(gBundleRef, ppp_msg, ppp_msg, langRef) ; } if (msg && CFStringGetLength(msg)) CFUserNotificationDisplayNotice(120, kCFUserNotificationNoteAlertLevel, gIconURLRef, NULL, gBundleURLRef, CFSTR("Internet Connect"), msg, NULL); if (msg) CFRelease(msg); if (ppp_msg) CFRelease(ppp_msg); if (dev_msg) CFRelease(dev_msg); if (langRef) CFRelease(langRef); } /* ----------------------------------------------------------------------------- changed for this ppp occured in configd cache ----------------------------------------------------------------------------- */ void ppp_updatesetup(struct ppp *ppp, CFDictionaryRef service) { // delay part or all the setup once all the notification for the service have been received ppp->dosetup = 1; return; } /* ----------------------------------------------------------------------------- changed for this ppp occured in configd cache ----------------------------------------------------------------------------- */ void ppp_postupdatesetup() { u_int32_t lval; struct ppp *ppp; TAILQ_FOREACH(ppp, &ppp_head, next) { if (ppp->dosetup) { ppp->dosetup = 0; ppp->needdispose = 0; switch (ppp->phase) { case PPP_IDLE: //printf("ppp_updatesetup : unit %d, PPP_IDLE\n", ppp->unit); if ((getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &lval) && lval) && (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &lval) || !lval || gLoggedInUser)) { ppp_doconnect(ppp, 0, 1, 0, 0); } break; case PPP_STATERESERVED: case PPP_HOLDOFF: // config has changed, dialondemand will need to be restarted ppp->setupchanged = 1; ppp_dodisconnect(ppp, 15, 0); break; default : // config has changed, dialondemand will need to be restarted ppp->setupchanged = 1; /* if ppp was started in dialondemand mode, then stop it */ // if (ppp->dialondemand) // ppp_dodisconnect(ppp, 15, 0); break; } } } return; } /* ----------------------------------------------------------------------------- call back from power management ----------------------------------------------------------------------------- */ void ppp_sleep(void * x, io_service_t y, natural_t messageType, void *messageArgument) { CFMutableDictionaryRef dict; SInt32 error; u_int32_t disc, allow, alerte, lval; CFDictionaryRef service = NULL; struct ppp *ppp; //printf("messageType %08lx, arg %08lx\n",(long unsigned int)messageType, (long unsigned int)messageArgument); switch ( messageType ) { case kIOMessageSystemWillSleep: gSleeping = 1; // time to sleep allow = 1; alerte = 0; TAILQ_FOREACH(ppp, &ppp_head, next) { if (ppp->phase != PPP_IDLE // by default, disconnect on sleep && (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectOnSleep, &disc) || disc || ppp->phase == PPP_STATERESERVED || ppp->phase == PPP_HOLDOFF)) { allow = 0; if (ppp->phase != PPP_STATERESERVED && ppp->phase != PPP_HOLDOFF) alerte = 1; ppp_dodisconnect(ppp, 15, 0); } } if (allow) { IOAllowPowerChange(gIOPort, (long)messageArgument); break; } gSleepArgument = (long)messageArgument; if (alerte) { dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (dict) { CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, gIconURLRef); CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, gBundleURLRef); CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, CFSTR("Internet Connect")); CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, CFSTR("Waiting for disconnection")); gSleepNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationNoteAlertLevel + kCFUserNotificationNoDefaultButtonFlag, &error, dict); } } break; case kIOMessageCanSystemSleep: // I refuse idle sleep if ppp is connected TAILQ_FOREACH(ppp, &ppp_head, next) { if (ppp->phase != PPP_IDLE && ppp->phase != PPP_STATERESERVED && ppp->phase != PPP_HOLDOFF) { IOCancelPowerChange(gIOPort, (long)messageArgument); return; } } IOAllowPowerChange(gIOPort, (long)messageArgument); break; case kIOMessageSystemWillNotSleep: /* should get it only if someone refuse an idle sleep but I don't have anything to do here */ break; case kIOMessageSystemHasPoweredOn: gSleeping = 0; // time to wakeup if (gSleepNotification) { CFUserNotificationCancel(gSleepNotification); CFRelease(gSleepNotification); gSleepNotification = 0; } TAILQ_FOREACH(ppp, &ppp_head, next) { service = copyEntity(kSCDynamicStoreDomainState, ppp->serviceID, kSCEntNetPPP); if (service) { ppp_updatestate(ppp, service); CFRelease(service); if (ppp->phase == PPP_IDLE) { if ((getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &lval) && lval) && (!getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &lval) || !lval || gLoggedInUser)) { ppp_doconnect(ppp, 0, 1, 0, 0); } } } } break; } } /* ----------------------------------------------------------------------------- user has looged out need to check the disconnect on logout flag for the ppp interfaces ----------------------------------------------------------------------------- */ int ppp_logout() { struct ppp *ppp; u_int32_t disc; TAILQ_FOREACH(ppp, &ppp_head, next) { if (ppp->phase != PPP_IDLE && getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &disc) && disc) ppp_dodisconnect(ppp, 15, 0); } return 0; } /* ----------------------------------------------------------------------------- user has logged in need to check the dialondemand flag again ----------------------------------------------------------------------------- */ int ppp_login() { struct ppp *ppp; u_int32_t val; TAILQ_FOREACH(ppp, &ppp_head, next) { if (ppp->phase == PPP_IDLE && getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &val) && val) ppp_doconnect(ppp, 0, 1, 0, 0); } return 0; } /* ----------------------------------------------------------------------------- user has switched need to check the disconnect on logout and dial on traffic flags for the ppp interfaces ----------------------------------------------------------------------------- */ int ppp_logswitch() { struct ppp *ppp; u_int32_t disc, val, demand; TAILQ_FOREACH(ppp, &ppp_head, next) { demand = getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDialOnDemand, &val) && val; switch (ppp->phase) { case PPP_IDLE: // rearm dial on demand if (demand) ppp_doconnect(ppp, 0, 1, 0, 0); break; default: if (getNumberFromEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectOnLogout, &disc) && disc) { // && (ppp->dialondemand == 0 // user initiated // || demand == 0)) { // system initiated but setting was removed // if dialondemand is set, it will need to be restarted ppp->setupchanged = demand; ppp_dodisconnect(ppp, 15, 0); } } } return 0; } /* ----------------------------------------------------------------------------- an interface is come down, dispose the ppp structure unit is the ppp managed unit, not the ifunit ----------------------------------------------------------------------------- */ int ppp_dispose(struct ppp *ppp) { // need to close the protocol first ppp_dodisconnect(ppp, 15, 0); if (ppp->phase != PPP_IDLE) { ppp->needdispose = 1; return 1; } TAILQ_REMOVE(&ppp_head, ppp, next); // then free the structure if (ppp->sid) free(ppp->sid); if (ppp->bundle) CFRelease(ppp->bundle); CFRelease(ppp->serviceID); CFRelease(ppp->subtypeRef); free(ppp); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void addparam(char **arg, u_int32_t *argi, char *param) { int len = strlen(param); if (len && (arg[*argi] = malloc(len + 1))) { strcpy(arg[*argi], param); (*argi)++; } } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void addintparam(char **arg, u_int32_t *argi, char *param, u_int32_t val) { u_char str[32]; addparam(arg, argi, param); sprintf(str, "%d", val); addparam(arg, argi, str); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void addstrparam(char **arg, u_int32_t *argi, char *param, char *val) { addparam(arg, argi, param); addparam(arg, argi, val); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_triggerdemand(struct ppp *ppp) { struct ifreq ifr; int s, i; //char str[16]; // CFStringGetCString(ppp->serviceID, str, sizeof(str), kCFStringEncodingUTF8); // printf("service '%s', ppp_triggerdemand\n", str); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return errno; memset (&ifr, 0, sizeof (ifr)); sprintf(ifr.ifr_name, "ppp%d", ppp->ifunit); if (ioctl(s, OSIOCGIFDSTADDR, &ifr) < 0) { close(s); return errno; } // just send a byte out there ((struct sockaddr_in *)&ifr.ifr_addr)->sin_port = 1; i = sendto(s, &s, 1, 0, (struct sockaddr *)&ifr.ifr_addr, sizeof(struct sockaddr_in)); close(s); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void escape_str(char *dst, int maxlen, char *src) { while (*src && (maxlen > 1)) { switch (*src) { case '\\': case '\"': case '\'': case ' ': *dst++ = '\\'; maxlen--; } *dst++ = *src++; maxlen--; } *dst = 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_doconnect(struct ppp *ppp, CFDictionaryRef options, u_int8_t dialondemand, void *client, int autoclose) { #define MAXARG 100 char str[MAXPATHLEN], str2[256], *cmdarg[MAXARG]; int needpasswd = 0, auth_default = 1, from_service; u_int32_t auth_bits = 0xF; /* PAP + CHAP + MSCHAP1 + MPCHAP2 */ u_char sopt[OPT_STR_LEN]; u_char pipearg[MAXPATHLEN]; u_int32_t len, lval, lval1, i, argi = 0; CFDictionaryRef service = NULL, pppdict = NULL, dict; CFArrayRef array = NULL; CFStringRef string = NULL; if (gSleeping) return EIO; // not the right time to dial // reset setup flag ppp->setupchanged = 0; switch (ppp->phase) { case PPP_IDLE: break; case PPP_STATERESERVED: // kill dormant process and post connection flag case PPP_HOLDOFF: //return ppp_triggerdemand(ppp); // since pppd is in dormant phase, it should immediatly quit. // FIx me : there is a small window of conflict where pppd is changing state, but it's // not yet published ppp->needconnectopts = options; if (options) CFRetain(options); ppp_dodisconnect(ppp, 15, 0); ppp->needconnect = 1; if (client) ppp_addclient(ppp, client, autoclose); ppp_setphase(ppp, PPP_INITIALIZE); return 0; break; default: if (client) { if ((ppp->needconnect && CFEqual(options, ppp->needconnectopts)) || (!ppp->needconnect && CFEqual(options, ppp->connectopts))) { ppp_addclient(ppp, client, autoclose); return 0; } } return EIO; // not the right time to dial } service = copyService(kSCDynamicStoreDomainSetup, ppp->serviceID); if (!service) return EIO; // that's bad pppdict = CFDictionaryGetValue(service, kSCEntNetPPP); if ((pppdict == 0) || (CFGetTypeID(pppdict) != CFDictionaryGetTypeID())) { CFRelease(service); return EIO; // that's bad too } pipearg[0] = 0; argi = 0; for (i = 0; i < MAXARG; i++) { cmdarg[i] = 0; } addparam(cmdarg, &argi, PPPD_PRGM); /* ************************************************************************* */ /* first Service ID option */ addstrparam(cmdarg, &argi, "serviceid", ppp->sid); /* ************************************************************************* */ /* then some basic admin options */ addintparam(cmdarg, &argi, "optionsfd", STDIN_FILENO); // add the dialog plugin if (gPluginsDir) { CFStringGetCString(gPluginsDir, str, sizeof(str), kCFStringEncodingUTF8); strcat(str, "PPPDialogs.ppp"); addstrparam(cmdarg, &argi, "plugin", str); } get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPVerboseLogging, options, service, &lval, 0); if (lval) addparam(cmdarg, &argi, "debug"); ppp_getoptval(ppp, options, service, PPP_OPT_ALERTENABLE, &ppp->alertenable, &len); /* ************************************************************************* */ if (ppp_getoptval(ppp, options, service, PPP_OPT_LOGFILE, sopt, &len) && sopt[0]) { // if logfile start with /, it's a full path // otherwise it's relative to the logs folder (convention) // we also strongly advise to name the file with the link number // for example ppplog0 // the default path is /var/log // it's useful to have the debug option with the logfile option // with debug option, pppd will log the negociation // debug option is different from kernel debug trace sprintf(str, "%s%s", sopt[0] == '/' ? "" : DIR_LOGS, sopt); addstrparam(cmdarg, &argi, "logfile", str); } /* ************************************************************************* */ /* then device specific options */ /* connect plugin parameter */ CFStringGetCString(ppp->subtypeRef, str2, sizeof(str2) - 4, kCFStringEncodingUTF8); strcat(str2, ".ppp"); // add plugin suffix addstrparam(cmdarg, &argi, "plugin", str2); if (ppp_getoptval(ppp, options, service, PPP_OPT_DEV_NAME, sopt, &len) && sopt[0]) addstrparam(cmdarg, &argi, "device", sopt); if (ppp_getoptval(ppp, options, service, PPP_OPT_DEV_SPEED, &lval, &len) && lval) { sprintf(str, "%d", lval); addparam(cmdarg, &argi, str); } switch (ppp->subtype) { case PPP_TYPE_SERIAL: // the controller has a built-in knowledge of serial connections /* PPPSerial currently supports Modem in case of Modem, the DeviceName key will contain the actual device, while the Modem dictionnary will contain the modem settings. if Hardware is undefined, or different from Modem, we expect to find external configuration files. (This is the case for ppp over ssh) */ get_str_option(ppp, kSCEntNetInterface, kSCPropNetInterfaceHardware, options, 0, sopt, &lval, ""); if (strcmp(sopt, "Modem")) { // we are done break; } if (ppp_getoptval(ppp, options, 0, PPP_OPT_DEV_CONNECTSCRIPT, sopt, &len) && sopt[0]) { // ---------- connect script parameter ---------- addstrparam(cmdarg, &argi, "modemscript", sopt); // add all the ccl flags get_int_option(ppp, kSCEntNetModem, kSCPropNetModemSpeaker, options, 0, &lval, 1); addparam(cmdarg, &argi, lval ? "modemsound" : "nomodemsound"); get_int_option(ppp, kSCEntNetModem, kSCPropNetModemErrorCorrection, options, 0, &lval, 1); addparam(cmdarg, &argi, lval ? "modemreliable" : "nomodemreliable"); get_int_option(ppp, kSCEntNetModem, kSCPropNetModemDataCompression, options, 0, &lval, 1); addparam(cmdarg, &argi, lval ? "modemcompress" : "nomodemcompress"); get_int_option(ppp, kSCEntNetModem, kSCPropNetModemPulseDial, options, 0, &lval, 0); addparam(cmdarg, &argi, lval ? "modempulse" : "modemtone"); // dialmode : 0 = normal, 1 = blind(ignoredialtone), 2 = manual lval = 0; ppp_getoptval(ppp, options, 0, PPP_OPT_DEV_DIALMODE, &lval, &len); addintparam(cmdarg, &argi, "modemdialmode", lval); } break; case PPP_TYPE_L2TP: string = get_cf_option(kSCEntNetL2TP, kSCPropNetL2TPTransport, CFStringGetTypeID(), options, service, 0); if (string) { if (CFStringCompare(string, kSCValNetL2TPTransportIP, 0) == kCFCompareEqualTo) addparam(cmdarg, &argi, "l2tpnoipsec"); } get_str_option(ppp, kSCEntNetL2TP, SCSTR("IPSecSharedSecret"), options, service, sopt, &lval, ""); if (sopt[0]) { strcat(pipearg, " l2tpipsecsharedsecret \""); /* we need to quote and escape the parameter */ escape_str(pipearg + strlen(pipearg), sizeof(pipearg) - strlen(pipearg) - 1, sopt); strcat(pipearg, "\""); //addstrparam(cmdarg, &argi, "l2tpipsecsharedsecret", sopt); } string = get_cf_option(kSCEntNetL2TP, SCSTR("IPSecSharedSecretEncryption"), CFStringGetTypeID(), options, service, 0); if (string) { if (CFStringCompare(string, CFSTR("Key"), 0) == kCFCompareEqualTo) addstrparam(cmdarg, &argi, "l2tpipsecsharedsecrettype", "key"); else if (CFStringCompare(string, CFSTR("Keychain"), 0) == kCFCompareEqualTo) addstrparam(cmdarg, &argi, "l2tpipsecsharedsecrettype", "keychain"); } get_int_option(ppp, SCSTR("L2TP"), SCSTR("UDPPort"), options, service, &lval, 0 /* Dynamic port */); addintparam(cmdarg, &argi, "l2tpudpport", lval); break; case PPP_TYPE_PPTP: get_int_option(ppp, SCSTR("PPTP"), SCSTR("TCPKeepAlive"), options, service, &lval, 0); if (lval) { get_int_option(ppp, SCSTR("PPTP"), SCSTR("TCPKeepAliveTimer"), options, service, &lval, 0); } else { /* option doesn't exist, piggy-back on lcp echo option */ ppp_getoptval(ppp, options, service, PPP_OPT_LCP_ECHO, &lval, &len); lval = lval >> 16; } addintparam(cmdarg, &argi, "pptp-tcp-keepalive", lval); break; } /* ************************************************************************* */ /* then COMM options */ if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_TERMINALMODE, &lval, &len)) { /* add the PPPSerial plugin if not already present Fix me : terminal mode is only supported in PPPSerial types of connection but subtype using ptys can use it the same way */ if (lval != PPP_COMM_TERM_NONE && ppp->subtype != PPP_TYPE_SERIAL) addstrparam(cmdarg, &argi, "plugin", "PPPSerial.ppp"); if (lval == PPP_COMM_TERM_WINDOW) addparam(cmdarg, &argi, "terminalwindow"); else if (lval == PPP_COMM_TERM_SCRIPT) if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_TERMINALSCRIPT, sopt, &len) && sopt[0]) addstrparam(cmdarg, &argi, "terminalscript", sopt); } /* ************************************************************************* */ // add generic phonenumber option if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_REMOTEADDR, sopt, &len) && sopt[0]) addstrparam(cmdarg, &argi, "remoteaddress", sopt); // ---------- add the redial options ---------- get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommRedialEnabled, options, service, &lval, 0); if (lval) { get_str_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommAlternateRemoteAddress, options, service, sopt, &lval, ""); if (sopt[0]) addstrparam(cmdarg, &argi, "altremoteaddress", sopt); get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommRedialCount, options, service, &lval, 0); if (lval) addintparam(cmdarg, &argi, "redialcount", lval); get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCommRedialInterval, options, service, &lval, 0); if (lval) addintparam(cmdarg, &argi, "redialtimer", lval); } /* ************************************************************************* */ if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_IDLETIMER, &lval, &len) && lval) { addintparam(cmdarg, &argi, "idle", lval); addparam(cmdarg, &argi, "noidlerecv"); } if (ppp_getoptval(ppp, options, service, PPP_OPT_COMM_SESSIONTIMER, &lval, &len) && lval) addintparam(cmdarg, &argi, "maxconnect", lval); /* ************************************************************************* */ /* then MISC options */ if (dialondemand) { addparam(cmdarg, &argi, "demand"); addintparam(cmdarg, &argi, "holdoff", 30); get_int_option(ppp, kSCEntNetPPP, SCSTR("MaxFailure"), 0, service, &lval, 3); addintparam(cmdarg, &argi, "maxfail", lval); } /* ************************************************************************* */ /* then LCP options */ // set echo option, so ppp hangup if we pull the modem cable // echo option is 2 bytes for interval + 2 bytes for failure if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_ECHO, &lval, &len) && lval) { if (lval >> 16) addintparam(cmdarg, &argi, "lcp-echo-interval", lval >> 16); if (lval & 0xffff) addintparam(cmdarg, &argi, "lcp-echo-failure", lval & 0xffff); } /* ************************************************************************* */ if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_HDRCOMP, &lval, &len)) { if (!(lval & 1)) addparam(cmdarg, &argi, "nopcomp"); if (!(lval & 2)) addparam(cmdarg, &argi, "noaccomp"); } /* ************************************************************************* */ if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_MRU, &lval, &len) && lval) addintparam(cmdarg, &argi, "mru", lval); /* ************************************************************************* */ if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_MTU, &lval, &len) && lval) addintparam(cmdarg, &argi, "mtu", lval); /* ************************************************************************* */ if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_RCACCM, &lval, &len) && lval) addintparam(cmdarg, &argi, "asyncmap", lval); else addparam(cmdarg, &argi, "receive-all"); /* ************************************************************************* */ if (ppp_getoptval(ppp, options, service, PPP_OPT_LCP_TXACCM, &lval, &len) && lval) { addparam(cmdarg, &argi, "escape"); str[0] = 0; for (lval1 = 0; lval1 < 32; lval1++) { if ((lval >> lval1) & 1) { sprintf(str2, "%d,", lval1); strcat(str, str2); } } str[strlen(str)-1] = 0; // remove last ',' addparam(cmdarg, &argi, str); } /* ************************************************************************* */ /* then IPCP options */ if (!existEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetIPv4)) { addparam(cmdarg, &argi, "noip"); } else { #if 0 if (getString(service, CFSTR("IPCPUpScript"), str, sizeof(str)) && str[0]) addstrparam(cmdarg, &argi, "ip-up", str); if (getString(service, CFSTR("IPCPDownScript"), str, sizeof(str)) && str[0]) addstrparam(cmdarg, &argi, "ip-down", str); #endif if (getStringFromEntity(kSCDynamicStoreDomainState, 0, kSCEntNetIPv4, kSCPropNetIPv4Router, sopt, OPT_STR_LEN) && sopt[0]) addstrparam(cmdarg, &argi, "ipparam", sopt); // OverridePrimary option not handled yet in Setup by IPMonitor get_int_option(ppp, kSCEntNetIPv4, kSCPropNetOverridePrimary, 0 /* don't look in options */, service, &lval, 0); if (lval) addparam(cmdarg, &argi, "defaultroute"); /* ************************************************************************* */ if (! (ppp_getoptval(ppp, options, service, PPP_OPT_IPCP_HDRCOMP, &lval, &len) && lval)) addparam(cmdarg, &argi, "novj"); /* ************************************************************************* */ /* XXX */ /* enforce the source address */ if (ppp->subtype == PPP_TYPE_L2TP || ppp->subtype == PPP_TYPE_PPTP ) { addintparam(cmdarg, &argi, "ip-src-address-filter", 2); } if (ppp_getoptval(ppp, options, service, PPP_OPT_IPCP_LOCALADDR, &lval, &len) && lval) sprintf(str2, "%d.%d.%d.%d", lval >> 24, (lval >> 16) & 0xFF, (lval >> 8) & 0xFF, lval & 0xFF); else strcpy(str2, "0"); strcpy(str, str2); strcat(str, ":"); if (ppp_getoptval(ppp, options, service, PPP_OPT_IPCP_REMOTEADDR, &lval, &len) && lval) sprintf(str2, "%d.%d.%d.%d", lval >> 24, (lval >> 16) & 0xFF, (lval >> 8) & 0xFF, lval & 0xFF); else strcpy(str2, "0"); strcat(str, str2); addparam(cmdarg, &argi, str); addparam(cmdarg, &argi, "noipdefault"); addparam(cmdarg, &argi, "ipcp-accept-local"); addparam(cmdarg, &argi, "ipcp-accept-remote"); /* ************************************************************************* */ // usepeerdns is there is a no dns value // setting 0 in the dns field will disable the usepeerdns option get_addr_option(ppp, kSCEntNetDNS, kSCPropNetDNSServerAddresses, options, service, &lval, -1); if (lval == -1) addparam(cmdarg, &argi, "usepeerdns"); } // of existEntity IPv4 /* ************************************************************************* */ /* then IPCPv6 options */ if (!existEntity(kSCDynamicStoreDomainSetup, ppp->serviceID, kSCEntNetIPv6)) { // ipv6 is not started by default } else { addparam(cmdarg, &argi, "+ipv6"); addparam(cmdarg, &argi, "ipv6cp-use-persistent"); } /* ************************************************************************* */ /* then ACSP options */ get_int_option(ppp, kSCEntNetPPP, SCSTR("ACSPEnabled"), options, service, &lval, 0); if (lval == 0) addparam(cmdarg, &argi, "noacsp"); /* ************************************************************************* */ /* then AUTH options */ // don't want authentication on our side... addparam(cmdarg, &argi, "noauth"); if (ppp_getoptval(ppp, options, service, PPP_OPT_AUTH_PROTO, &lval, &len) && (lval != PPP_AUTH_NONE)) { if (ppp_getoptval(ppp, options, service, PPP_OPT_AUTH_NAME, sopt, &len) && sopt[0]) { addstrparam(cmdarg, &argi, "user", sopt); lval1 = get_str_option(ppp, kSCEntNetPPP, kSCPropNetPPPAuthPassword, options, service, sopt, &lval, ""); if (sopt[0]) { /* if the option was from the setup, check for encryption method */ if ((lval1 == 3) && (string = CFDictionaryGetValue(pppdict, kSCPropNetPPPAuthPasswordEncryption)) && (CFGetTypeID(string) == CFStringGetTypeID()) && (CFStringCompare(string, SCSTR("Keychain"), 0) == kCFCompareEqualTo)) { addstrparam(cmdarg, &argi, "keychainpassword", sopt); } else { strcat(pipearg, " password \""); /* we need to quote and escape the parameter */ escape_str(pipearg + strlen(pipearg), sizeof(pipearg) - strlen(pipearg) - 1, sopt); strcat(pipearg, "\""); //addstrparam(cmdarg, &argi, "password", sopt); } } else { /* we need to prompt for a password if we have a username and no saved password */ needpasswd = 1; } } } // load authentication protocols array = 0; if (options) { dict = CFDictionaryGetValue(options, kSCEntNetPPP); if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) array = CFDictionaryGetValue(dict, kSCPropNetPPPAuthProtocol); } if ((array == 0) || (CFGetTypeID(array) != CFArrayGetTypeID())) { array = CFDictionaryGetValue(pppdict, kSCPropNetPPPAuthProtocol); } if (array && (CFGetTypeID(array) == CFArrayGetTypeID()) && (lval = CFArrayGetCount(array))) { auth_default = 0; auth_bits = 0; // clear bits for (i = 0; i < lval; i++) { string = CFArrayGetValueAtIndex(array, i); if (string && (CFGetTypeID(string) == CFStringGetTypeID())) { if (CFStringCompare(string, kSCValNetPPPAuthProtocolPAP, 0) == kCFCompareEqualTo) auth_bits |= 1; else if (CFStringCompare(string, kSCValNetPPPAuthProtocolCHAP, 0) == kCFCompareEqualTo) auth_bits |= 2; else if (CFStringCompare(string, CFSTR("MSCHAP1") /*kSCValNetPPPAuthProtocolMSCHAP1*/ , 0) == kCFCompareEqualTo) auth_bits |= 4; else if (CFStringCompare(string, CFSTR("MSCHAP2") /*kSCValNetPPPAuthProtocolMSCHAP2*/, 0) == kCFCompareEqualTo) auth_bits |= 8; else if (CFStringCompare(string, CFSTR("EAP") /*kSCValNetPPPAuthProtocolEAP*/, 0) == kCFCompareEqualTo) auth_bits |= 0x10; } } } // check EAP plugins if (auth_bits & 0x10) { auth_bits &= ~0x10; // clear EAP flag array = 0; from_service = 0; if (options) { dict = CFDictionaryGetValue(options, kSCEntNetPPP); if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) array = CFDictionaryGetValue(dict, CFSTR("AuthEAPPlugins") /*kSCPropNetPPPAuthEAPPlugins*/); } if ((array == 0) || (CFGetTypeID(array) != CFArrayGetTypeID())) { array = CFDictionaryGetValue(pppdict, CFSTR("AuthEAPPlugins") /*kSCPropNetPPPAuthEAPPlugins*/); from_service = 1; } if (array && (CFGetTypeID(array) == CFArrayGetTypeID()) && (lval = CFArrayGetCount(array))) { for (i = 0; i < lval; i++) { string = CFArrayGetValueAtIndex(array, i); if (string && (CFGetTypeID(string) == CFStringGetTypeID())) { CFStringGetCString(string, str, sizeof(str) - 4, kCFStringEncodingUTF8); // for user options, we only accept plugin in the EAP directory (/System/Library/Extensions) if (from_service || strchr(str, '\\') == 0) { strcat(str, ".ppp"); // add plugin suffix addstrparam(cmdarg, &argi, "eapplugin", str); auth_bits |= 0x10; // confirm EAP flag } } } } } get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPCCPEnabled, options, service, &lval, 0); if (lval // Fix me : to enforce use of MS-CHAP, refuse any alteration of default auth proto // a dialer specifying PAP or CHAP will works without CCP/MPPE // even is CCP is enabled in the configuration. // Will be revisited when addition compression modules and // authentication modules will be added && ppp_getoptval(ppp, options, service, PPP_OPT_AUTH_PROTO, &lval, &len) && (lval == OPT_AUTH_PROTO_DEF)) { // Fix me : mppe is the only currently supported compression // if the CCPAccepted and CCPRequired array are not there, // assume we accept all types of compression we support addparam(cmdarg, &argi, "mppe-stateless"); addparam(cmdarg, &argi, "mppe-128"); addparam(cmdarg, &argi, "mppe-40"); // No authentication specified, also enforce the use of MS-CHAP if (auth_default) auth_bits = 0xc; /* MSCHAP 1 and 2 only */ } else { // no compression protocol addparam(cmdarg, &argi, "noccp"); } // set authentication protocols parameters if ((auth_bits & 1) == 0) addparam(cmdarg, &argi, "refuse-pap"); if ((auth_bits & 2) == 0) addparam(cmdarg, &argi, "refuse-chap-md5"); if ((auth_bits & 4) == 0) addparam(cmdarg, &argi, "refuse-mschap"); if ((auth_bits & 8) == 0) addparam(cmdarg, &argi, "refuse-mschap-v2"); if ((auth_bits & 0x10) == 0) addparam(cmdarg, &argi, "refuse-eap"); // if EAP is the only method, pppd doesn't need to ask for the password // let the EAP plugin handle that. // if there is an other protocol than EAP, then we still need to prompt for password if (auth_bits == 0x10) needpasswd = 0; // loop local traffic destined to the local ip address // Radar #3124639. //addparam(cmdarg, &argi, "looplocal"); if (!(ppp->alertenable & PPP_ALERT_PASSWORDS) || !needpasswd) addparam(cmdarg, &argi, "noaskpassword"); get_str_option(ppp, kSCEntNetPPP, kSCPropNetPPPAuthPrompt, options, service, sopt, &lval, ""); if (sopt[0]) { str2[0] = 0; CFStringGetCString(kSCValNetPPPAuthPromptAfter, str2, sizeof(str2), kCFStringEncodingUTF8); if (!strcmp(sopt, str2)) addparam(cmdarg, &argi, "askpasswordafter"); } /* ************************************************************************* */ // no need for pppd to detach. addparam(cmdarg, &argi, "nodetach"); // reminder option must be specified after PPPDialogs plugin option get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPIdleReminder, options, service, &lval, 0); if (lval) { get_int_option(ppp, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, options, service, &lval, 0); if (lval) addintparam(cmdarg, &argi, "reminder", lval); } /* add any additional plugin we want to load */ array = CFDictionaryGetValue(pppdict, kSCPropNetPPPPlugins); if (array && (CFGetTypeID(array) == CFArrayGetTypeID())) { lval = CFArrayGetCount(array); for (i = 0; i < lval; i++) { string = CFArrayGetValueAtIndex(array, i); if (string && (CFGetTypeID(string) == CFStringGetTypeID())) { CFStringGetCString(string, str, sizeof(str) - 4, kCFStringEncodingUTF8); strcat(str, ".ppp"); // add plugin suffix addstrparam(cmdarg, &argi, "plugin", str); } } } // always try to use options defined in /etc/ppp/peers/[service provider] // they can override what have been specified by the PPPController // be careful to the conflicts on options get_str_option(ppp, kSCEntNetPPP, kSCPropUserDefinedName, options, service, sopt, &lval, ""); if (sopt[0]) addstrparam(cmdarg, &argi, "call", sopt); /* ************************************************************************* */ CFRelease(service); #if 0 printf("/usr/sbin/pppd "); for (i = 1; i < MAXARG; i++) { if (cmdarg[i]) { printf("%d : %s\n", i, cmdarg[i]); //printf("%s ", cmdarg[i]); } } printf("\n"); #endif ppp->lastdevstatus = 0; if ((pipe(ppp->iFD) == -1) || ((ppp->pid = _SCDPluginExecCommand2(exec_callback, (void*)ppp_makeref(ppp), 0, 0, PATH_PPPD, cmdarg, exec_postfork, (void*)ppp_makeref(ppp))) == -1)) { ppp->laststatus = EXIT_FATAL_ERROR; ppp_display_error(ppp); } else { write(ppp->iFD[WRITE], pipearg, strlen(pipearg)); close(ppp->iFD[WRITE]); if (client) ppp_addclient(ppp, client, autoclose); ppp->laststatus = EXIT_OK; ppp->started_link = 1; ppp->oldphase = PPP_INITIALIZE; ppp_setphase(ppp, PPP_INITIALIZE); ppp->dialondemand = dialondemand; ppp->connectopts = options; if (options) CFRetain(options); } for (i = 0; i < MAXARG; i++) { if (cmdarg[i]) { free(cmdarg[i]); cmdarg[i] = 0; } } return ppp->laststatus; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ static void exec_postfork(pid_t pid, void *arg) { struct ppp *ppp = ppp_findbyref((u_int32_t)arg); if (ppp == 0) // the service has disappeared return; if (pid) { /* if parent */ int yes = 1; close(ppp->iFD[READ]); if (ioctl(ppp->iFD[WRITE], FIONBIO, &yes) == -1) { // printf("ioctl(,FIONBIO,): %s\n", strerror(errno)); } } else { /* if child */ int i; close(ppp->iFD[WRITE]); if (ppp->iFD[READ] != STDIN_FILENO) { dup2(ppp->iFD[READ], STDIN_FILENO); } close(STDOUT_FILENO); open(_PATH_DEVNULL, O_RDWR, 0); close(STDERR_FILENO); open(_PATH_DEVNULL, O_RDWR, 0); /* close any other open FDs */ for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) close(i); } return; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void exec_callback(pid_t pid, int status, struct rusage *rusage, void *context) { struct ppp *ppp; u_int32_t error; if (status == 0) // everything is fine... return; ppp = ppp_findbyref((u_int32_t)context); if (ppp == 0) // the service has disappeared return; if (ppp->pid != pid) // callback for a previous process. return; if (ppp->started_link == 0) // ignore the callback. return; /* an error occured while parsing arguments */ /* the dynamic store will not be updated */ /* we also don't want to restart dialondemand */ ppp->laststatus = (status >> 8) & 0xFF; ppp_setphase(ppp, PPP_IDLE); ppp->started_link = 0; ppp->dialondemand = 0; ppp_display_error(ppp); if (ppp->connectopts) { CFRelease(ppp->connectopts); ppp->connectopts = 0; } error = ppp_translate_error(ppp->subtype, ppp->laststatus, 0); client_notify(ppp->sid, ppp_makeref(ppp), PPP_EVT_DISCONNECTED, error, 1); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_clientgone(void *client) { struct ppp *ppp; struct ppp_client *pppclient; /* arbitration mechanism */ TAILQ_FOREACH(ppp, &ppp_head, next) { pppclient = ppp_getclient(ppp, client); if (pppclient && pppclient->autoclose) { ppp_dodisconnect(ppp, 15, client); } } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_dodisconnect(struct ppp *ppp, int sig, void *client) { int pid, dokill = 1; /* arbitration mechanism : disconnects only when no client is using it */ if (client) { if (ppp_getclient(ppp, client)) ppp_delclient(ppp, client); /* check if we have at least one client */ if (TAILQ_FIRST(&ppp->client_head)) return 0; } else { ppp_delclients(ppp); } if (ppp->phase != PPP_IDLE && !ppp->kill_link && !ppp->kill_sent) { // anticipate next phase switch (ppp->phase) { case PPP_INITIALIZE: if (ppp->needconnect) { ppp->needconnect = 0; if (ppp->needconnectopts) CFRelease(ppp->needconnectopts); ppp->needconnectopts = 0; } dokill = 0; // we can have race condition with ccl execution, so kill it again later // no break; case PPP_CONNECTLINK: ppp_setphase(ppp, PPP_DISCONNECTLINK); break; case PPP_DISCONNECTLINK: case PPP_TERMINATE: return 0; default: ppp_setphase(ppp, PPP_TERMINATE); } if (dokill && getNumberFromEntity(kSCDynamicStoreDomainState, ppp->serviceID, kSCEntNetPPP, CFSTR("pid") /*kSCPropNetPPPpid*/, &pid)) { //printf("ppp_dodisconnect : unit %d, kill pid %d\n", ppp->unit, pid); ppp->started_link = 0; ppp->kill_sent = 1; kill(pid, sig); } else { //printf("ppp_dodisconnect : kill later unit %d\n", ppp->unit); ppp->kill_link = sig; // kill it later } } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_dosuspend(struct ppp *ppp) { int pid; if (ppp->phase != PPP_IDLE && getNumberFromEntity(kSCDynamicStoreDomainState, ppp->serviceID, kSCEntNetPPP, CFSTR("pid") /*kSCPropNetPPPpid*/, &pid)) { kill(pid, SIGTSTP); } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_doresume(struct ppp *ppp) { int pid; if (ppp->phase != PPP_IDLE && getNumberFromEntity(kSCDynamicStoreDomainState, ppp->serviceID, kSCEntNetPPP, CFSTR("pid") /*kSCPropNetPPPpid*/, &pid)) { kill(pid, SIGCONT); } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_addclient(struct ppp *ppp, void *client, int autoclose) { struct ppp_client *pppclient; pppclient = malloc(sizeof(struct ppp_client)); if (pppclient == 0) return -1; // very bad... pppclient->client = client; pppclient->autoclose = autoclose; TAILQ_INSERT_TAIL(&ppp->client_head, pppclient, next); return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_delclient(struct ppp *ppp, void *client) { struct ppp_client *pppclient; TAILQ_FOREACH(pppclient, &ppp->client_head, next) { if (pppclient->client == client) { TAILQ_REMOVE(&ppp->client_head, pppclient, next); free(pppclient); return 0; } } return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ struct ppp_client *ppp_getclient(struct ppp *ppp, void *client) { struct ppp_client *pppclient; TAILQ_FOREACH(pppclient, &ppp->client_head, next) if (pppclient->client == client) return pppclient; return 0; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int ppp_delclients(struct ppp *ppp) { struct ppp_client *pppclient; while (pppclient = TAILQ_FIRST(&ppp->client_head)) { TAILQ_REMOVE(&ppp->client_head, pppclient, next); free(pppclient); } return 0; }