1/*
2 * Copyright (c) 2000 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    This program implements a PPP central server, listening for incoming calls
26    and forking/execing pppd processes to handle the incoming sessions.
27    This is primarily designed to imnplement the VPN server, hence is name 'vpnd'.
28
29    The architecture of this program, and in particular the plugin management, is
30    largely inspired by pppd.
31
32*/
33
34#include <stdio.h>
35#include <ctype.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39#include <signal.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <syslog.h>
43#include <netdb.h>
44#include <paths.h>
45#include <sys/queue.h>
46
47#include <sys/param.h>
48#include <sys/types.h>
49#include <sys/wait.h>
50#include <sys/time.h>
51#include <sys/resource.h>
52#include <sys/stat.h>
53#include <sys/socket.h>
54#include <sys/sysctl.h>
55#include <net/if.h>
56#include <netinet/in.h>
57#include <arpa/inet.h>
58#include <libgen.h>
59
60#include <CoreFoundation/CoreFoundation.h>
61#include <SystemConfiguration/SystemConfiguration.h>
62#include "../../Family/if_ppplink.h"
63
64#include "vpnd.h"
65#include "vpnoptions.h"
66#include "vpnplugins.h"
67#include "cf_utils.h"
68#include "ipsec_utils.h"
69
70
71// ----------------------------------------------------------------------------
72//	Globals
73// ----------------------------------------------------------------------------
74char 		*no_ppp_msg = "This system lacks PPP kernel support\n";
75static char	pid_path[MAXPATHLEN] = _PATH_VARRUN DAEMON_NAME "-";	// pid file path - the rest will be filled in later
76// ----------------------------------------------------------------------------
77//	Private Globals
78// ----------------------------------------------------------------------------
79
80static volatile int rcvd_sig_child = 0;
81static volatile int rcvd_sig_hup = 0;
82static volatile int rcvd_sig_usr1 = 0;
83static volatile int terminate = 0;
84static int forwarding = -1;
85static FILE	*logfile = 0;
86static int stdio_is_valid = 1;
87static struct vpn_params *params;
88
89static int spawn(struct vpn_params *params);
90static void close_file_descriptors(void);
91static void redirect_std_file(void);
92static void detach(void);
93static void write_pid_file(struct vpn_params *params);
94static void delete_pid_file(void);
95static void sig_chld(int inSignal);
96static void sig_term(int inSignal);
97static void sig_hup(int inSignal);
98static void setup_signal_handlers(void);
99static int set_forwarding(int *oldval, int newval);
100static void create_log_file(char *inLogPath);
101static void close_log_file(void);
102void vpnlog(int nSyslogPriority, char *format_str, ...);
103static void dump_params(struct vpn_params *params);
104
105// ----------------------------------------------------------------------------
106//	main
107// ----------------------------------------------------------------------------
108int main (int argc, char *argv[])
109{
110
111    int 		i;
112
113    /*
114     * Check that we are running as root.
115     */
116    if (geteuid() != 0) {
117	fprintf(stderr, "must be root to run %s, since it is not setuid-root\n", argv[0]);
118	exit(EXIT_NOT_ROOT);
119    }
120
121
122    params = (struct vpn_params*)malloc(sizeof (struct vpn_params));
123    if (params == 0)
124        exit(EXIT_FATAL_ERROR);
125    bzero(params, sizeof(*params));
126
127    // Close all non-standard file descriptors.
128    close_file_descriptors();
129
130    /*
131     * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else.
132     * This way we can close 0, 1, 2 in detach() without clobbering
133     * a fd that we are using.
134     */
135    if ((i = open("/dev/null", O_RDWR)) >= 0) {
136	while (0 <= i && i <= 2)
137	    i = dup(i);
138	if (i >= 0)
139	    close(i);
140    }
141
142    /*
143     * open syslog facility.
144     */
145    openlog("vpnd", LOG_PID | LOG_NDELAY, LOG_RAS);
146
147    /*
148     * read and process options.
149     */
150    if (process_options(params, argc, argv))
151        exit(EXIT_OPTION_ERROR);
152
153    /* Check if ppp is available in the kernel */
154    if (!ppp_available()) {
155	vpnlog(LOG_ERR, "The PPP kernel extension could not be loaded\n");
156	exit(EXIT_NO_KERNEL_SUPPORT);
157    }
158
159    // if no server id option - read active server list and
160    // launch a vpnd process for each
161    if (params->server_id == 0) {
162        close_file_descriptors();   // close all but std fd
163        redirect_std_file();        // redirect std
164        spawn(params);
165        exit(EXIT_OK);
166    }
167
168    vpnlog(LOG_NOTICE, "Server '%s' starting...\n", params->server_id);
169
170    open_dynamic_store(params); 	// open a connection to the dynamic store
171    init_address_lists();		// init the address lists
172
173    if (process_prefs(params)) {	// prepare launch args
174    	vpnlog(LOG_ERR, "Error processing prefs file\n");
175		// <rdar://problem/7052049> return ok, otherwise launchd will most likely restart vpnd with the same erroneous prefs
176        exit(EXIT_OK);
177    }
178    if (check_conflicts(params))	// check if another server of this type already running
179        exit(EXIT_OPTION_ERROR);
180
181    if (kill_orphans(params))		// check for and kill orphan pppd processes running with the same
182        exit(EXIT_FATAL_ERROR);		// server id left over from a crashed vpnd process.
183
184    if (params->log_path)
185   	create_log_file(params->log_path);
186
187    if (init_plugin(params)) {
188        vpnlog(LOG_ERR, "Initialization of vpnd plugin failed\n");
189        exit(EXIT_FATAL_ERROR);
190    }
191    if (get_plugin_args(params, 0)) {
192    	vpnlog(LOG_ERR, "Error getting arguments from plugin\n");
193        exit(EXIT_OPTION_ERROR);
194    }
195
196    if (params->debug)
197        dump_params(params);
198
199    setuid(geteuid());
200
201    // OK, everything looks good. Daemonize now and redirect std fd's.
202    if (params->daemonize) {
203        stdio_is_valid = 0;
204		close_dynamic_store(params);	// close it now, re-open after detach
205        detach();
206        setsid();
207        redirect_std_file();
208        open_dynamic_store(params);	// re-open after detach
209        vpnlog(LOG_NOTICE, "Server '%s' moved to background\n", params->server_id);
210    }
211
212    setup_signal_handlers();
213    write_pid_file(params);	/* Write out our pid like a good little daemon */
214    publish_state(params);
215
216    /* activate IP forwarding */
217    if (set_forwarding(&forwarding, 1))
218        vpnlog(LOG_ERR, "Cannot activate IP forwarding (error %d)\n", errno);
219
220    /* now listen for connections*/
221    vpnlog(LOG_NOTICE, "Listening for connections...\n");
222    accept_connections(params);
223
224    /* restore IP forwarding to former state */
225    if (set_forwarding(0, forwarding))
226        vpnlog(LOG_ERR, "Cannot reset IP forwarding (error %d)\n", errno);
227
228    close_log_file();
229    delete_pid_file();
230    vpnlog(LOG_NOTICE, "Server '%s' stopped\n", params->server_id);
231
232    exit(EXIT_OK) ;
233}
234
235//-----------------------------------------------------------------------------
236// 	spawn - launch a copy of vpnd for each active server id
237//-----------------------------------------------------------------------------
238static int spawn(struct vpn_params *params)
239{
240    int             i, j, count;
241    char            server[OPT_STR_LEN];
242    pid_t           pidChild = 0;
243    char            *args[6];
244    char            *vpnd_prog = VPND_PRGM;
245    char            *debug_opt = "-d";
246    char            *no_detach_opt = "-x";
247    char            *server_id_opt = "-i";
248    CFArrayRef      active_servers;
249    CFStringRef     string;
250
251    // setup the common args
252    server[0] = 0;
253    args[0] = vpnd_prog;
254    i = 1;
255    if (params->debug)
256        args[i++] = debug_opt;
257    if (params->daemonize == 0)
258        args[i++] = no_detach_opt;
259    args[i++] = server_id_opt;
260    args[i++] = server;
261    args[i] = 0;	// teminating zero
262
263    if ((active_servers = get_active_servers(params)) == 0)
264        return 0;       // no active servers
265    count = CFArrayGetCount(active_servers);
266
267    // find and launch active server ids
268    for (j = 0; j < count; j++) {
269        string = CFArrayGetValueAtIndex(active_servers, j);
270        if (isString(string))
271            CFStringGetCString(string, server, OPT_STR_LEN, kCFStringEncodingMacRoman);
272        else
273            continue;   // skip it
274
275        switch (pidChild = fork ()) {
276            case 0:		// in child
277                execve(PATH_VPND, args, NULL);		// launch it
278                break;
279            case -1:		// error
280                vpnlog(LOG_ERR, "Attempt to fork new vpnd failed\n") ;
281                return -1;
282            default:
283                vpnlog(LOG_INFO, "Launched vpnd process id '%d' for server id '%s'\n", pidChild, server);
284                break;
285        }
286    }
287    CFRelease(active_servers);
288    return 0;
289}
290
291//-----------------------------------------------------------------------------
292// 	change the ip forwarding setting.
293//-----------------------------------------------------------------------------
294static int set_forwarding(int *oldval, int newval)
295{
296    size_t len = sizeof(int);
297
298    if (newval != 0 && newval != 1)
299        return 0;	// ignore the command
300
301    return sysctlbyname("net.inet.ip.forwarding", oldval, &len, &newval, sizeof(int));
302}
303
304//-----------------------------------------------------------------------------
305// 	close_file_descriptors - closes all file descriptors except std.
306//-----------------------------------------------------------------------------
307static void close_file_descriptors(void)
308{
309    // Find the true upper limit on file descriptors.
310    register int	i = OPEN_MAX;
311    register int	nMin = STDERR_FILENO;
312    struct rlimit	lim;
313
314    if (!getrlimit (RLIMIT_NOFILE, &lim))
315        i = lim.rlim_cur;
316    else
317        vpnlog(LOG_ERR, "close_file_descriptors() - getrlimit() failed\n");
318
319    // Close all file descriptors except std*.
320    while (--i > nMin)
321        close (i) ;
322}
323
324//-----------------------------------------------------------------------------
325//	redirect_std_file - redirect standard file descriptors to /dev/null.
326//-----------------------------------------------------------------------------
327static void redirect_std_file(void)
328{
329    int	i;
330
331    // Handle stdin
332    i = open(_PATH_DEVNULL, O_RDONLY);
333    if ((i != -1) && (i != STDIN_FILENO)) {
334        dup2(i, STDIN_FILENO);
335        close (i);
336    }
337
338    // Handle stdout and stderr
339    i = open (_PATH_DEVNULL, O_WRONLY);
340
341    if (i != STDOUT_FILENO)
342        dup2(i, STDOUT_FILENO);
343    if (i != STDERR_FILENO)
344        dup2(i, STDERR_FILENO);
345
346    if ((i != -1) && (i != STDOUT_FILENO) && (i != STDERR_FILENO))
347        close(i);
348
349}
350
351
352//-----------------------------------------------------------------------------
353//	detach - closes all file descriptors for this process.
354//-----------------------------------------------------------------------------
355static void detach(void)
356{
357    errno = 0;
358    switch (fork()) {
359        case 0:		// in child
360            break;
361        case -1:	// error
362            vpnlog(LOG_ERR, "Detach failed: errno= %d\n", errno);
363            /* FALLTHRU */
364        default:	// parent
365            exit(errno) ;
366    }
367}
368
369
370//-----------------------------------------------------------------------------
371//	write_pid_file - writes out standard pid file at given path.
372//-----------------------------------------------------------------------------
373static void write_pid_file(struct vpn_params *params)
374{
375    FILE 	*pidfile;
376    char	subtype[OPT_STR_LEN];
377
378    subtype[0] = 0;
379    if (params->serverSubTypeRef)
380		CFStringGetCString(params->serverSubTypeRef, subtype, OPT_STR_LEN, kCFStringEncodingUTF8);
381    if (subtype[0])
382        strlcat(pid_path, subtype, sizeof(pid_path));
383    strlcat(pid_path, ".pid", sizeof(pid_path));
384
385    if ((pidfile = fopen(pid_path, "w")) != NULL) {
386	fprintf(pidfile, "%d\n", getpid());
387	fclose(pidfile);
388    } else
389	vpnlog(LOG_WARNING, "Failed to create pid file %s: %m", pid_path);
390}
391
392//-----------------------------------------------------------------------------
393//	delete_pid_file
394//-----------------------------------------------------------------------------
395static void delete_pid_file(void)
396{
397    remove(pid_path);
398}
399
400// ----------------------------------------------------------------------------
401//	Log File Functions
402// ----------------------------------------------------------------------------
403static char *log_time_string(time_t inNow, char *inTimeString, int maxLen)
404{
405    struct tm *tmTime;
406
407    if (!inTimeString)
408            return 0;
409
410    tmTime = localtime(&inNow);
411
412    snprintf(inTimeString, maxLen, "%04d-%02d-%02d %02d:%02d:%02d %s",
413                            tmTime->tm_year + 1900, tmTime->tm_mon + 1, tmTime->tm_mday,
414                            tmTime->tm_hour, tmTime->tm_min, tmTime->tm_sec,
415                            tmTime->tm_zone);
416    return inTimeString;
417}
418
419// ----------------------------------------------------------------------------
420//	create_log_file
421// ----------------------------------------------------------------------------
422static void create_log_file(char *inLogPath)
423{
424    char	theTime[40];
425	mode_t	mask;
426
427    // Setup the log file.
428
429	mask = umask(S_IWGRP|S_IXGRP|S_IWOTH|S_IXOTH);
430	logfile = fopen(inLogPath, "a");
431	mask = umask(mask);
432
433	/* if the first fopen fails, then it's likely to be due to an inexistant directory path.
434	 * Create the directory subtree and try fopen again
435	 */
436    if (logfile == 0)
437	{
438		char *dirPath = dirname(inLogPath);
439		vpnlog(LOG_WARNING, "Warning: Creating directory for log file = %s\n", inLogPath);
440		makepath(dirPath);
441		logfile = fopen(inLogPath, "a");
442	}
443
444	if(logfile == 0)
445	{
446		vpnlog(LOG_ERR, "Could not open log file = %s\n", inLogPath);
447	}
448    else {
449        fcntl(fileno(logfile), F_SETFD, 1);
450
451        // Add the file header only when first created.
452        //if (!ftell(logfile))
453        //	fprintf(logfile, "#Version: 1.0\n#Software: %s, build %s\n",
454        //					inSoftware, inVersion);
455
456        // Always add time stamp and field ID
457        fprintf(logfile, "#Start-Date: %s\n"
458                                        "#Fields: date time s-comment\n",
459                                        log_time_string(time(NULL), theTime, sizeof(theTime)));
460        fflush(logfile);
461    }
462}
463
464// ----------------------------------------------------------------------------
465//	close_log_file
466// ----------------------------------------------------------------------------
467static void close_log_file(void)
468{
469    char the_time[40];
470
471    if (!logfile)
472        return;
473
474    fprintf (logfile, "#End-Date: %s\n", log_time_string(time(NULL), the_time, sizeof(the_time))) ;
475    fclose(logfile) ;
476}
477
478// ----------------------------------------------------------------------------
479//	debug_log
480// ----------------------------------------------------------------------------
481void vpnlog(int nSyslogPriority, char *format_str, ...)
482{
483    va_list		args;
484    time_t		tNow;
485    struct tm		*tmTime;
486    char		theTime[40];
487
488    if (!params->debug && LOG_PRI(nSyslogPriority) == LOG_DEBUG)
489        return;
490
491    tNow = time(NULL);
492    tmTime = localtime(&tNow);
493
494    // If the facility hasn't been defined, make it LOG_DAEMON.
495    //if (!(nSyslogPriority & LOG_FACMASK))
496    //    nSyslogPriority |= LOG_DAEMON;
497
498    snprintf(theTime, sizeof(theTime), "%04d-%02d-%02d %02d:%02d:%02d %s\t",
499             tmTime->tm_year + 1900, tmTime->tm_mon + 1, tmTime->tm_mday,
500             tmTime->tm_hour, tmTime->tm_min, tmTime->tm_sec,
501             tmTime->tm_zone);
502
503    if (logfile) {
504        va_start(args, format_str);
505        fputs(theTime, logfile);
506        vfprintf(logfile, format_str, args);
507        fflush(logfile);
508        va_end(args);
509    }
510
511    va_start(args, format_str);
512    vsyslog(nSyslogPriority, format_str, args);
513    va_end(args);
514
515    // Log to stderr if the socket is valid
516    //	AND ( we're debugging OR this is a high priority message)
517    if (stdio_is_valid && (params->debug || (LOG_PRI(nSyslogPriority) >= LOG_WARNING))) {
518        va_start(args, format_str);
519        fputs(theTime, stderr);
520        vfprintf(stderr, format_str, args);
521        fflush(stderr);
522        va_end(args);
523    }
524
525}
526
527//-----------------------------------------------------------------------------
528//	dump_params
529//-----------------------------------------------------------------------------
530static void dump_params(struct vpn_params *params)
531{
532    int	i;
533	char	*subtype = 0, *servertype = "Unknown";
534
535	switch (params->server_type) {
536		case SERVER_TYPE_IPSEC:
537			servertype = "IPSec";
538			break;
539		case SERVER_TYPE_PPP:
540			servertype = "PPP";
541			switch (params->server_subtype) {
542				case PPP_TYPE_SERIAL:
543					subtype = "Serial";
544					break;
545				case PPP_TYPE_PPPoE:
546					subtype = "PPPoE";
547					break;
548				case PPP_TYPE_PPTP:
549					subtype = "PPTP";
550					break;
551				case PPP_TYPE_L2TP:
552					subtype = "L2TP";
553					break;
554				default:
555					subtype = "Unknown";
556					break;
557			}
558			break;
559	}
560
561    vpnlog(LOG_DEBUG, "params->daemonize = %d\n", params->daemonize);
562    vpnlog(LOG_DEBUG, "params->max_sessions = %d\n", params->max_sessions);
563    vpnlog(LOG_DEBUG, "params->server_id = %s\n", params->server_id);
564    vpnlog(LOG_DEBUG, "params->server_type = %s\n", servertype);
565    if (subtype)
566		vpnlog(LOG_DEBUG, "params->server_subtype = %s\n", subtype);
567
568    vpnlog(LOG_DEBUG, "params->lb_enable = %d\n", params->lb_enable);
569    if (params->lb_enable) {
570		char str[32];
571		//vpnlog(LOG_DEBUG, "params->lb_priority = %d\n", params->lb_priority);
572		vpnlog(LOG_DEBUG, "params->lb_interface = %s\n", params->lb_interface);
573		inet_ntop(AF_INET, &params->lb_cluster_address, str, sizeof(str));
574		vpnlog(LOG_DEBUG, "params->lb_cluster_address = %s\n", str);
575		inet_ntop(AF_INET, &params->lb_redirect_address, str, sizeof(str));
576		vpnlog(LOG_DEBUG, "params->lb_redirect_address = %s\n", str);
577		vpnlog(LOG_DEBUG, "params->lb_port = %d\n", ntohs(params->lb_port));
578	}
579
580    if (params->plugin_path)
581		vpnlog(LOG_DEBUG, "params->plugin_path = %s\n", params->plugin_path);
582    vpnlog(LOG_DEBUG, "params->log_path = %s\n", params->log_path);
583    if (params->next_arg_index)
584		vpnlog(LOG_DEBUG, "params->next_arg_index = %d\n", params->next_arg_index);
585
586    for (i = 0; i < params->next_arg_index; i++)
587        vpnlog(LOG_DEBUG, "params->exec_args[%d] = %s\n", i, params->exec_args[i]);
588
589}
590
591//-----------------------------------------------------------------------------
592//	set_terminate
593//-----------------------------------------------------------------------------
594void set_terminate(void)
595{
596    terminate = 1;
597}
598
599//-----------------------------------------------------------------------------
600//	got_terminate
601//-----------------------------------------------------------------------------
602int got_terminate(void)
603{
604    return terminate;
605}
606
607//-----------------------------------------------------------------------------
608//	got_sig_chld
609//-----------------------------------------------------------------------------
610int got_sig_chld(void)
611{
612    if (rcvd_sig_child) {
613        rcvd_sig_child = 0;
614        return 1;
615    }
616    return 0;
617}
618
619//-----------------------------------------------------------------------------
620//	got_sig_hup
621//-----------------------------------------------------------------------------
622int got_sig_hup(void)
623{
624    if (rcvd_sig_hup) {
625        rcvd_sig_hup = 0;
626        return 1;
627    }
628    return 0;
629}
630
631//-----------------------------------------------------------------------------
632//	got_sig_usr1
633//-----------------------------------------------------------------------------
634int got_sig_usr1(void)
635{
636    if (rcvd_sig_usr1) {
637        rcvd_sig_usr1 = 0;
638        return 1;
639    }
640    return 0;
641}
642
643
644
645//-----------------------------------------------------------------------------
646//	setup_signal_handlers
647//-----------------------------------------------------------------------------
648static void sig_chld(int inSignal)
649{
650    rcvd_sig_child = 1;
651}
652
653static void sig_term(int inSignal)
654{
655    vpnlog(LOG_INFO, "terminating on signal %d\n", inSignal);
656    terminate = 1;
657}
658
659static void sig_hup(int inSignal)
660{
661    rcvd_sig_hup = 1;
662}
663
664static void sig_usr1(int inSignal)
665{
666    rcvd_sig_usr1 = 1;
667}
668
669
670static void setup_signal_handlers(void)
671{
672    // Setup the signal handlers.
673    struct sigaction	sSigAction, sSigOldAction;
674
675    sigemptyset (&sSigAction.sa_mask);
676    // Not sure if we always want to restart system calls...
677    sSigAction.sa_flags = 0;	//SA_RESTART;
678
679    sSigAction.sa_handler = sig_term;
680    sigaction(SIGTERM, &sSigAction, &sSigOldAction);
681
682    sSigAction.sa_handler = sig_term;
683    sigaction(SIGINT, &sSigAction, &sSigOldAction);
684
685    sSigAction.sa_handler = sig_hup;
686    sigaction(SIGHUP, &sSigAction, &sSigOldAction);
687
688    sSigAction.sa_handler = sig_usr1;
689    sigaction(SIGUSR1, &sSigAction, &sSigOldAction);
690
691    sSigAction.sa_flags |= SA_NOCLDSTOP;	//%%%% do we want this ??
692    sSigAction.sa_handler = sig_chld;
693    sigaction(SIGCHLD, &sSigAction, &sSigOldAction);
694}
695
696//-----------------------------------------------------------------------------
697// 	toggle_debug
698//-----------------------------------------------------------------------------
699void toggle_debug(void)
700{
701	if (params->debug) {
702		vpnlog(LOG_DEBUG, "debugging disabled\n");
703		params->debug = 0;
704	} else {
705		params->debug = 1;
706		vpnlog(LOG_DEBUG, "debugging enabled - dumping current parameters\n");
707		dump_params(params);
708	}
709}
710
711//-----------------------------------------------------------------------------
712// 	update_prefs
713//-----------------------------------------------------------------------------
714int update_prefs(void)
715{
716    int 		i;
717    struct vpn_params 	*old_params;
718	int		debug = params->debug;
719
720
721	// need to update existing params structure because ptr
722	// to this struct is passed to accept_connections function
723
724    old_params = (struct vpn_params*)malloc(sizeof(struct vpn_params));
725    if (old_params == 0) {
726        vpnlog(LOG_ERR, "Could not allocate memory for old preferences");
727        return -1;
728    }
729
730    // save current parameters and clear params struct
731    *old_params = *params;
732	bzero(params, sizeof(*params));
733	params->debug = old_params->debug;
734	params->server_id = old_params->server_id;
735	params->daemonize = old_params->daemonize;
736
737	begin_address_update();
738
739    if (process_prefs(params)) {
740    	vpnlog(LOG_ERR, "Update preferences - Error processing prefs file\n");
741        goto fail;
742	}
743
744    // check for consistency
745    if (params->server_subtype != old_params->server_subtype) {
746        vpnlog(LOG_ERR, "Update preferences - server subtype does not match\n");
747        goto fail;
748    }
749
750    // get and check plugin args
751    if (get_plugin_args(params, 1)) {
752        vpnlog(LOG_ERR, "Update preferences - plugin arguments invalid or inconsistent\n");
753        goto fail;
754    }
755
756	//
757    // success
758    //
759    CFRelease(old_params->serverSubTypeRef);
760    CFRelease(old_params->serverRef);
761    CFRelease(old_params->serverIDRef);
762    if (old_params->plugin_path)
763		free(old_params->plugin_path);
764    for (i = 0; i < old_params->next_arg_index; i++)
765        free(old_params->exec_args[i]);
766    free(old_params);
767    apply_address_update();
768    vpnlog(LOG_INFO, "Update of preferences succeeded - settings have been changed\n");
769    if (debug)
770        dump_params(params);
771    return 0;
772
773fail:
774    cancel_address_update();
775	if (debug)
776		dump_params(params);
777	if (params->serverSubTypeRef)
778		CFRelease(params->serverSubTypeRef);
779	if (params->serverRef)
780		CFRelease(params->serverRef);
781	if (params->serverIDRef)
782		CFRelease(params->serverIDRef);
783    if (params->plugin_path)
784		free(params->plugin_path);
785    if (params->plugin_path)
786		free(params->plugin_path);
787	for (i = 0; i < params->next_arg_index; i++)
788		free(params->exec_args[i]);
789	*params = *old_params;
790	free(old_params);
791
792    vpnlog(LOG_ERR, "Update of preferences failed - settings left unchanged\n");
793    return 0;
794
795}
796
797