1/* 2 * Copyright (c) 2000-2003 Apple Computer, 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 * Theory of operation : 27 * 28 * PPPoE plugin for vpnd 29 * 30----------------------------------------------------------------------------- */ 31 32/* ----------------------------------------------------------------------------- 33 Includes 34----------------------------------------------------------------------------- */ 35 36#include <stdio.h> 37#include <ctype.h> 38#include <stdlib.h> 39#include <string.h> 40#include <unistd.h> 41#include <signal.h> 42#include <errno.h> 43#include <fcntl.h> 44#include <syslog.h> 45#include <netdb.h> 46#include <pwd.h> 47#include <setjmp.h> 48#include <sys/param.h> 49#include <sys/types.h> 50#include <sys/wait.h> 51#include <sys/time.h> 52#include <sys/resource.h> 53#include <sys/socket.h> 54#include <sys/stat.h> 55#include <sys/socket.h> 56#include <netinet/in.h> 57#include <arpa/inet.h> 58#include <syslog.h> 59#include <sys/ioctl.h> 60#include <net/if.h> 61#include <net/route.h> 62#include <pthread.h> 63#include <sys/kern_event.h> 64#include <netinet/in_var.h> 65#include <CoreFoundation/CFNumber.h> 66#include <CoreFoundation/CFBundle.h> 67#include <SystemConfiguration/SystemConfiguration.h> 68 69#define APPLE 1 70 71#include "../../../Family/ppp_defs.h" 72#include "../../../Family/if_ppp.h" 73#include "../../../Family/ppp_domain.h" 74#include "../../../Helpers/vpnd/vpnd.h" 75#include "../../../Helpers/vpnd/vpnplugins.h" 76#include "../../../Helpers/vpnd/RASSchemaDefinitions.h" 77#include "../../../Helpers/vpnd/cf_utils.h" 78#include "../PPPoE-extension/PPPoE.h" 79 80 81 82// ---------------------------------------------------------------------------- 83// � Private Globals 84// ---------------------------------------------------------------------------- 85 86static CFBundleRef bundle = 0; 87static int listen_sockfd = -1; 88static char device[17]; 89static CFStringRef service = NULL; 90static CFStringRef access_concentrator = NULL; 91 92#define PPPOE_NKE "PPPoE.kext" 93#define PPPOE_NKE_ID "com.apple.nke.pppoe" 94 95int pppoevpn_get_pppd_args(struct vpn_params *params, int reload); 96int pppoevpn_listen(void); 97int pppoevpn_accept(void); 98int pppoevpn_refuse(void); 99void pppoevpn_close(void); 100 101static u_long load_kext(char *kext, int byBundleID); 102 103 104/* ----------------------------------------------------------------------------- 105plugin entry point, called by vpnd 106ref is the vpn bundle reference 107pppref is the ppp bundle reference 108bundles can be layout in two different ways 109- As simple vpn bundles (bundle.vpn). the bundle contains the vpn bundle binary. 110- As full ppp bundles (bundle.ppp). The bundle contains the ppp bundle binary, 111and also the vpn kext and the vpn bundle binary in its Plugins directory. 112if a simple vpn bundle was used, pppref will be NULL. 113if a ppp bundle was used, the vpn plugin will be able to get access to the 114Plugins directory and load the vpn kext. 115----------------------------------------------------------------------------- */ 116int start(struct vpn_channel* the_vpn_channel, CFBundleRef ref, CFBundleRef pppref, int debug, int log_verbose) 117{ 118 int s; 119 char name[MAXPATHLEN]; 120 CFURLRef url; 121 122 /* first load the kext if we are loaded as part of a ppp bundle */ 123 if (pppref) { 124 s = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE); 125 if (s < 0) { 126 if ((url = CFBundleCopyBundleURL(pppref))) { 127 name[0] = 0; 128 CFURLGetFileSystemRepresentation(url, 0, (UInt8 *)name, MAXPATHLEN - 1); 129 CFRelease(url); 130 strlcat(name, "/", sizeof(name)); 131 if ((url = CFBundleCopyBuiltInPlugInsURL(pppref))) { 132 CFURLGetFileSystemRepresentation(url, 0, (UInt8 *)(name + strlen(name)), 133 MAXPATHLEN - strlen(name) - strlen(PPPOE_NKE) - 1); 134 CFRelease(url); 135 strlcat(name, "/", sizeof(name)); 136 strlcat(name, PPPOE_NKE, sizeof(name)); 137#if !TARGET_OS_EMBEDDED // This file is not built for Embedded 138 if (!load_kext(name, 0)) 139#else 140 if (!load_kext(PPPOE_NKE_ID, 1)) 141#endif 142 s = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE); 143 } 144 } 145 if (s < 0) { 146 vpnlog(LOG_ERR, "PPPoE plugin: Unable to load PPPoE kernel extension\n"); 147 return -1; 148 } 149 } 150 close (s); 151 } 152 153 /* retain reference */ 154 bundle = ref; 155 CFRetain(bundle); 156 157 // hookup our socket handlers 158 bzero(the_vpn_channel, sizeof(struct vpn_channel)); 159 the_vpn_channel->get_pppd_args = pppoevpn_get_pppd_args; 160 the_vpn_channel->listen = pppoevpn_listen; 161 the_vpn_channel->accept = pppoevpn_accept; 162 the_vpn_channel->refuse = pppoevpn_refuse; 163 the_vpn_channel->close = pppoevpn_close; 164 165 /* copy default interface */ 166 strlcpy(device, "en0", sizeof(device)); 167 168 return 0; 169} 170 171/* ----------------------------------------------------------------------------- 172 pppoevpn_get_pppd_args 173----------------------------------------------------------------------------- */ 174int pppoevpn_get_pppd_args(struct vpn_params *params, int reload) 175{ 176 CFStringRef string; 177 178 if (params->serverRef) { 179 /* arguments from the preferences file */ 180 addstrparam(params->exec_args, ¶ms->next_arg_index, "pppoemode", "answer"); 181 182 if ((string = get_cfstr_option(params->serverRef, kRASEntPPPoE, kRASPropPPPoEDeviceName))) { 183 if (!CFStringGetCString(string, device, sizeof(device), kCFStringEncodingUTF8)) { 184 vpnlog(LOG_ERR, "PPPoE plugin: Could not get device name\n"); 185 return -1; 186 } 187 } 188 addstrparam(params->exec_args, ¶ms->next_arg_index, "device", device); 189 190 if ((service = get_cfstr_option(params->serverRef, kRASEntPPPoE, kRASPropPPPoEServiceName))) 191 CFRetain(service); 192 193 if ((access_concentrator = get_cfstr_option(params->serverRef, kRASEntPPPoE, kRASPropPPPoEAccessConcentratorName))) 194 CFRetain(access_concentrator); 195 } 196 197 return 0; 198} 199 200 201/* ----------------------------------------------------------------------------- 202 system call wrappers 203----------------------------------------------------------------------------- */ 204int pppoe_sys_accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen) 205{ 206 int fd; 207 208 while ((fd = accept(sockfd, cliaddr, addrlen)) == -1) 209 if (errno != EINTR) { 210 vpnlog(LOG_ERR, "PPPoE plugin: error calling accept = %s\n", strerror(errno)); 211 return -1; 212 } 213 return fd; 214} 215 216int pppoe_sys_close(int sockfd) 217{ 218 while (close(sockfd) == -1) 219 if (errno != EINTR) { 220 vpnlog(LOG_ERR, "PPPoE plugin: error calling close on socket = %s\n", strerror(errno)); 221 return -1; 222 } 223 return 0; 224} 225 226 227/* ----------------------------------------------------------------------------- 228 closeall 229----------------------------------------------------------------------------- */ 230static void closeall() 231{ 232 int i; 233 234 for (i = getdtablesize() - 1; i >= 0; i--) close(i); 235 open("/dev/null", O_RDWR, 0); 236 dup(0); 237 dup(0); 238 return; 239} 240 241 242/* ----------------------------------------------------------------------------- 243 load_kext 244----------------------------------------------------------------------------- */ 245u_long load_kext(char *kext, int byBundleID) 246{ 247 int pid; 248 249 if ((pid = fork()) < 0) 250 return 1; 251 252 if (pid == 0) { 253 closeall(); 254 // PPP kernel extension not loaded, try load it... 255 if (byBundleID) 256 execle("/sbin/kextload", "kextload", "-b", kext, (char *)0, (char *)0); 257 else 258 execle("/sbin/kextload", "kextload", kext, (char *)0, (char *)0); 259 exit(1); 260 } 261 262 while (waitpid(pid, 0, 0) < 0) { 263 if (errno == EINTR) 264 continue; 265 return 1; 266 } 267 return 0; 268} 269 270 271/* ----------------------------------------------------------------------------- 272 pppoevpn_listen() called by vpnd to setup listening socket 273----------------------------------------------------------------------------- */ 274int pppoevpn_listen(void) 275{ 276 277 struct sockaddr_pppoe addr; 278 int s, error = -1; 279 struct ifreq ifr; 280 281 /* first, make sure the interface is UP */ 282 error = -1; 283 s = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); 284 if (s >= 0) { 285 bzero(&ifr, sizeof(ifr)); 286 bcopy(device, ifr.ifr_name, strlen(device)); 287 if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0) { 288 // ensure that the device is UP 289 ifr.ifr_flags |= IFF_UP; 290 if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) >= 0) 291 error = 0 ; 292 } 293 close(s); 294 } 295 if (error) { 296 vpnlog(LOG_ERR, "PPPoE plugin: Could not configure the interface UP - err = %s\n", strerror(errno)); 297 goto fail; 298 } 299 300 // Create the requested socket 301 while ((listen_sockfd = socket (PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE)) < 0) { 302 if (errno != EINTR) { 303 vpnlog(LOG_ERR, "PPPoE plugin: Could not create socket - err = %s\n", strerror(errno)); 304 goto fail; 305 } 306 } 307 308 if (setsockopt(listen_sockfd, PPPPROTO_PPPOE, PPPOE_OPT_INTERFACE, device, strlen(device))) { 309 vpnlog(LOG_ERR, "PPPoE plugin: Could not specify listening interface '%s' - err = %s\n", device, strerror(errno)); 310 goto fail; 311 } 312 313 if (access_concentrator || service) { 314 bzero(&addr, sizeof(addr)); 315 addr.ppp.ppp_len = sizeof(struct sockaddr_pppoe); 316 addr.ppp.ppp_family = AF_PPP; 317 addr.ppp.ppp_proto = PPPPROTO_PPPOE; 318 if (access_concentrator) 319 CFStringGetCString(access_concentrator, addr.pppoe_ac_name, sizeof(addr.pppoe_ac_name), kCFStringEncodingUTF8); 320 if (service) 321 CFStringGetCString(service, addr.pppoe_service, sizeof(addr.pppoe_service), kCFStringEncodingUTF8); 322 323 if (bind(listen_sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_pppoe)) < 0) { 324 vpnlog(LOG_ERR, "PPPoE plugin: bind failed for service = '%s', access concentrator = '%s'. Error = %s\n", 325 service, access_concentrator, strerror(errno)); 326 goto fail; 327 } 328 } 329 330 while (listen(listen_sockfd, SOMAXCONN) < 0) { 331 if (errno == EINTR) { 332 vpnlog(LOG_ERR, "PPPoE plugin: error calling listen = %s\n", strerror(errno)); 333 goto fail; 334 } 335 } 336 337 return listen_sockfd; 338 339fail: 340 if (listen_sockfd!= -1) { 341 close(listen_sockfd); 342 listen_sockfd = -1; 343 } 344 return -1; 345} 346 347 348/* ----------------------------------------------------------------------------- 349 pppoevpn_accept() called by vpnd to listen for incomming connections. 350----------------------------------------------------------------------------- */ 351int pppoevpn_accept(void) 352{ 353 354 int fdConn; 355 struct sockaddr_storage ssSender; 356 struct sockaddr *sapSender = (struct sockaddr *)&ssSender; 357 socklen_t nSize = sizeof(ssSender); 358 359 if ((fdConn = pppoe_sys_accept(listen_sockfd, sapSender, &nSize)) < 0) 360 return -1; 361 if (sapSender->sa_family != AF_PPP) { 362 vpnlog(LOG_ERR, "PPPoE plugin: Unexpected protocol family!\n"); 363 if (pppoe_sys_close(fdConn) < 0) 364 return -1; 365 return 0; 366 } 367 368 return fdConn; 369} 370 371/* ----------------------------------------------------------------------------- 372 pppoevpn_refuse() called by vpnd to refuse incomming connections 373 374 return values: -1 error 375 0 handled - do not launch pppd 376----------------------------------------------------------------------------- */ 377int pppoevpn_refuse(void) 378{ 379 380 int fdConn; 381 struct sockaddr_pppoe ssSender; 382 struct sockaddr *sapSender = (struct sockaddr *)&ssSender; 383 socklen_t nSize = sizeof (ssSender); 384 385 if ((fdConn = pppoe_sys_accept(listen_sockfd, sapSender, &nSize)) < 0) 386 return -1; 387 388 if (pppoe_sys_close(fdConn) < 0) 389 return -1; 390 391 return 0; 392} 393 394/* ----------------------------------------------------------------------------- 395 pppoevpn_close() called by vpnd to close listening socket and cleanup. 396----------------------------------------------------------------------------- */ 397void pppoevpn_close(void) 398{ 399 if (listen_sockfd != -1) { 400 pppoe_sys_close(listen_sockfd); 401 listen_sockfd = -1; 402 } 403} 404 405