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 *  PPTP 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#include "../../../Helpers/vpnd/vpnplugins.h"
70#include "../../../Helpers/vpnd/vpnd.h"
71#include "../PPTP-extension/PPTP.h"
72#include "../PPTP-plugin/pptp.h"
73
74
75
76// ----------------------------------------------------------------------------
77//	� Private Globals
78// ----------------------------------------------------------------------------
79static CFBundleRef 	bundle = 0;
80static int 		listen_sockfd = -1;
81
82int pptpvpn_get_pppd_args(struct vpn_params *params, int reload);
83int pptpvpn_listen(void);
84int pptpvpn_accept(void);
85int pptpvpn_refuse(void);
86void pptpvpn_close(void);
87
88static u_long load_kext(char *kext, int byBundleID);
89
90
91/* -----------------------------------------------------------------------------
92plugin entry point, called by vpnd
93ref is the vpn bundle reference
94pppref is the ppp bundle reference
95bundles can be layout in two different ways
96- As simple vpn bundles (bundle.vpn). the bundle contains the vpn bundle binary.
97- As full ppp bundles (bundle.ppp). The bundle contains the ppp bundle binary,
98and also the vpn kext and the vpn bundle binary in its Plugins directory.
99if a simple vpn bundle was used, pppref will be NULL.
100if a ppp bundle was used, the vpn plugin will be able to get access to the
101Plugins directory and load the vpn kext.
102----------------------------------------------------------------------------- */
103int start(struct vpn_channel* the_vpn_channel, CFBundleRef ref, CFBundleRef pppref, int debug, int log_verbose)
104{
105    int 	s;
106    char 	name[MAXPATHLEN];
107    CFURLRef	url;
108
109    /* first load the kext if we are loaded as part of a ppp bundle */
110    if (pppref) {
111        s = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPTP);
112        if (s < 0) {
113            if ((url = CFBundleCopyBundleURL(pppref))) {
114                name[0] = 0;
115                CFURLGetFileSystemRepresentation(url, 0, (UInt8 *)name, MAXPATHLEN - 1);
116                CFRelease(url);
117                strlcat(name, "/", sizeof(name));
118                if ((url = CFBundleCopyBuiltInPlugInsURL(pppref))) {
119                    CFURLGetFileSystemRepresentation(url, 0, (UInt8 *)(name + strlen(name)),
120                                MAXPATHLEN - strlen(name) - strlen(PPTP_NKE) - 1);
121                    CFRelease(url);
122                    strlcat(name, "/", sizeof(name));
123                    strlcat(name, PPTP_NKE, sizeof(name));
124#if !TARGET_OS_EMBEDDED  // This file is not built for Embedded
125                    if (!load_kext(name, 0))
126#else
127                    if (!load_kext(PPTP_NKE_ID, 1))
128#endif
129                        s = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPTP);
130                }
131            }
132            if (s < 0) {
133                vpnlog(LOG_ERR, "PPTP plugin: Unable to load PPTP kernel extension\n");
134                return -1;
135            }
136        }
137        close (s);
138    }
139
140    /* retain reference */
141    bundle = ref;
142    CFRetain(bundle);
143
144    // hookup our socket handlers
145    bzero(the_vpn_channel, sizeof(struct vpn_channel));
146    the_vpn_channel->get_pppd_args = pptpvpn_get_pppd_args;
147    the_vpn_channel->listen = pptpvpn_listen;
148    the_vpn_channel->accept = pptpvpn_accept;
149    the_vpn_channel->refuse = pptpvpn_refuse;
150    the_vpn_channel->close = pptpvpn_close;
151
152    return 0;
153}
154
155/* -----------------------------------------------------------------------------
156    pptpvpn_get_pppd_args
157----------------------------------------------------------------------------- */
158int pptpvpn_get_pppd_args(struct vpn_params *params, int reload)
159{
160    if (params->serverRef)
161        /* arguments from the preferences file */
162        addstrparam(params->exec_args, &params->next_arg_index, "pptpmode", "answer");
163
164    return 0;
165}
166
167
168/* -----------------------------------------------------------------------------
169    system call wrappers
170----------------------------------------------------------------------------- */
171int pptp_sys_accept(int sockfd, struct sockaddr *cliaddr, int *addrlen)
172{
173    int fd;
174
175    while ((fd = accept(sockfd, cliaddr, (uint32_t *)addrlen)) == -1)
176        if (errno != EINTR) {
177            vpnlog(LOG_ERR, "PPTP plugin: error calling accept = %s\n", strerror(errno));
178            return -1;
179        }
180    return fd;
181}
182
183int pptp_sys_close(int sockfd)
184{
185    while (close(sockfd) == -1)
186        if (errno != EINTR) {
187            vpnlog(LOG_ERR, "PPTP plugin: error calling close on socket = %s\n", strerror(errno));
188            return -1;
189        }
190    return 0;
191}
192
193
194/* -----------------------------------------------------------------------------
195    closeall
196----------------------------------------------------------------------------- */
197static void closeall()
198{
199    int i;
200
201    for (i = getdtablesize() - 1; i >= 0; i--) close(i);
202    open("/dev/null", O_RDWR, 0);
203    dup(0);
204    dup(0);
205    return;
206}
207
208
209/* -----------------------------------------------------------------------------
210    load_kext
211----------------------------------------------------------------------------- */
212u_long load_kext(char *kext, int byBundleID)
213{
214    int pid;
215
216    if ((pid = fork()) < 0)
217        return 1;
218
219    if (pid == 0) {
220        closeall();
221        // PPP kernel extension not loaded, try load it...
222		if (byBundleID)
223			execle("/sbin/kextload", "kextload", "-b", kext, (char *)0, (char *)0);
224		else
225			execle("/sbin/kextload", "kextload", kext, (char *)0, (char *)0);
226        exit(1);
227    }
228
229    while (waitpid(pid, 0, 0) < 0) {
230        if (errno == EINTR)
231            continue;
232       return 1;
233    }
234    return 0;
235}
236
237/* -----------------------------------------------------------------------------
238    pptpvpn_listen()  called by vpnd to setup listening socket
239----------------------------------------------------------------------------- */
240int pptpvpn_listen(void)
241{
242
243    struct sockaddr_in	addrListener;
244    int			val;
245
246    // Create the requested socket
247    while ((listen_sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
248        if (errno != EINTR) {
249            vpnlog(LOG_ERR, "PPTP plugin: Could not create socket - err = %s\n", strerror(errno));
250            return -1 ;
251    }
252
253    // Don't want children to have a copy of this.
254//    while (fcntl(listen_sockfd, F_SETFD, 1) == -1)
255//        if (errno != EINTR) {
256//            syslog(LOG_ERR, "VPND PPTP plugin: error calling fcntl = %s\n", strerror(errno));
257//            return -1;
258//        }
259
260    addrListener.sin_family = AF_INET;
261    addrListener.sin_addr.s_addr = htonl(INADDR_ANY);
262    addrListener.sin_port = htons(PPTP_TCP_PORT);
263
264    val = 1;
265    setsockopt(listen_sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
266
267    while (bind(listen_sockfd, (struct sockaddr *) &addrListener, sizeof (addrListener)) < 0)
268        if (errno != EINTR) {
269            vpnlog(LOG_ERR, "PPTP plugin: Unable to bind socket to port %d - err = %s\n",
270                        PPTP_TCP_PORT, strerror(errno));
271            return -1;
272        }
273
274    while (listen(listen_sockfd, SOMAXCONN) < 0)
275        if (errno == EINTR) {
276            vpnlog(LOG_ERR, "PPTP plugin: error calling listen = %s\n", strerror(errno));
277            return -1;
278        }
279
280    return listen_sockfd;
281}
282
283
284/* -----------------------------------------------------------------------------
285    pptpvpn_accept() called by vpnd to listen for incomming connections.
286----------------------------------------------------------------------------- */
287int pptpvpn_accept(void)
288{
289
290    int				fdConn;
291    struct sockaddr_storage	ssSender;
292    struct sockaddr		*sapSender = (struct sockaddr *)&ssSender;
293    int				nSize = sizeof (ssSender);
294
295    if ((fdConn = pptp_sys_accept(listen_sockfd, sapSender, &nSize)) < 0)
296            return -1;
297    if (sapSender->sa_family != AF_INET) {
298        vpnlog(LOG_ERR, "PPTP plugin: Unexpected protocol family!\n");
299        if (pptp_sys_close(fdConn) < 0)
300            return -1;
301        return 0;
302    }
303
304    return fdConn;
305}
306
307/* -----------------------------------------------------------------------------
308    pptpvpn_refuse() called by vpnd to refuse incomming connections
309
310        return values:  -1		error
311                        0		handled - do not launch pppd
312----------------------------------------------------------------------------- */
313int pptpvpn_refuse(void)
314{
315
316    int				fdConn;
317    struct sockaddr_storage	ssSender;
318    struct sockaddr		*sapSender = (struct sockaddr *)&ssSender;
319    int				nSize = sizeof (ssSender);
320
321    if ((fdConn = pptp_sys_accept(listen_sockfd, sapSender, &nSize)) < 0)
322        return -1;
323
324    if (pptp_sys_close(fdConn) < 0)
325        return -1;
326
327    return 0;
328}
329
330/* -----------------------------------------------------------------------------
331    pptpvpn_close()  called by vpnd to close listening socket and cleanup.
332----------------------------------------------------------------------------- */
333void pptpvpn_close(void)
334{
335    if (listen_sockfd != -1) {
336        pptp_sys_close(listen_sockfd);
337        listen_sockfd = -1;
338    }
339}
340
341