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, &params->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, &params->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