#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <netdb.h>
#include <utmp.h>
#include <paths.h>
#include <sys/queue.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include "../../Family/if_ppplink.h"
#include "vpnd.h"
#include "vpnoptions.h"
#include "vpnplugins.h"
#include "RASSchemaDefinitions.h"
#include "cf_utils.h"
static int process_interface_prefs(struct vpn_params *params);
static int process_ipv4_prefs(struct vpn_params *params);
static int process_ipv6_prefs(struct vpn_params *params);
static int process_dns_prefs(struct vpn_params *params);
static int process_ppp_prefs(struct vpn_params *params);
void ppp_process_options(struct vpn_params *params)
{
params->plugin_path = 0;
params->serverSubTypeRef = 0;
params->server_subtype = PPP_TYPE_OTHER;
}
int ppp_process_prefs(struct vpn_params *params)
{
char pathStr[MAXPATHLEN];
u_int32_t len;
int i;
params->next_arg_index = 0;
for (i = 0; i < MAXARG; i++)
params->exec_args[i] = 0;
addparam(params->exec_args, ¶ms->next_arg_index, PPPD_PRGM);
addstrparam(params->exec_args, ¶ms->next_arg_index, "serverid", params->server_id); addparam(params->exec_args, ¶ms->next_arg_index, "nodetach"); addparam(params->exec_args, ¶ms->next_arg_index, "proxyarp");
if (process_interface_prefs(params))
goto fail;
if (process_ipv4_prefs(params))
goto fail;
if (process_ipv6_prefs(params))
goto fail;
if (process_dns_prefs(params))
goto fail;
if (process_ppp_prefs(params))
goto fail;
get_str_option(params->serverRef, kRASEntPPP, kRASPropUserDefinedName, pathStr, &len, "");
if (pathStr[0])
addstrparam(params->exec_args, ¶ms->next_arg_index, "call", pathStr);
return 0;
fail:
if (params->serverSubTypeRef) {
CFRelease(params->serverSubTypeRef);
params->serverSubTypeRef = 0;
}
return -1;
}
static int process_interface_prefs(struct vpn_params *params)
{
CFDictionaryRef dict;
dict = CFDictionaryGetValue(params->serverRef, kRASEntInterface);
params->serverSubTypeRef = CFDictionaryGetValue(dict, kRASPropInterfaceSubType);
if (!isString(params->serverSubTypeRef)) {
vpnlog(LOG_ERR, "Incorrect server subtype found\n");
return -1;
}
params->plugin_path = malloc(CFStringGetLength(params->serverSubTypeRef) + 5);
CFStringGetCString(params->serverSubTypeRef, params->plugin_path,
CFStringGetLength(params->serverSubTypeRef) + 5, kCFStringEncodingUTF8);
strcat(params->plugin_path, ".ppp");
if (!plugin_exists(params->plugin_path)) {
vpnlog(LOG_ERR, "Unsupported plugin '%s'\n", params->plugin_path);
return -1;
}
addstrparam(params->exec_args, ¶ms->next_arg_index, "plugin", params->plugin_path);
if (CFStringCompare(params->serverSubTypeRef, kRASValInterfaceSubTypePPTP, 0) == kCFCompareEqualTo)
params->server_subtype = PPP_TYPE_PPTP;
else if (CFStringCompare(params->serverSubTypeRef, kRASValInterfaceSubTypeL2TP, 0) == kCFCompareEqualTo)
params->server_subtype = PPP_TYPE_L2TP;
else if (CFStringCompare(params->serverSubTypeRef, kRASValInterfaceSubTypePPPoE, 0) == kCFCompareEqualTo)
params->server_subtype = PPP_TYPE_PPPoE;
else if (CFStringCompare(params->serverSubTypeRef, kRASValInterfaceSubTypePPPSerial, 0) == kCFCompareEqualTo)
params->server_subtype = PPP_TYPE_SERIAL;
else
params->server_subtype = PPP_TYPE_OTHER;
CFRetain(params->serverSubTypeRef);
return 0;
}
static int process_ipv4_prefs(struct vpn_params *params)
{
CFArrayRef array = 0;
CFStringRef ipstr = 0, ipstr2 = 0;
CFDictionaryRef dict;
char str[MAXPATHLEN];
int i, nb, len;
char ipcstr[100], ipcstr2[100], ip_addr[100], ip_addr2[100];
char *ip, *ip2;
if ((dict = CFDictionaryGetValue(params->serverRef, kRASEntIPv4)) && isDictionary(dict)) {
get_array_option(params->serverRef, kRASEntIPv4, kRASPropIPv4Addresses, 0, str, &len, "");
if (str[0] == 0) {
ipstr = CopyDefaultIPAddress();
if (ipstr) {
CFStringGetCString(ipstr, str, sizeof(str), kCFStringEncodingMacRoman);
CFRelease(ipstr);
ipstr = 0;
}
}
if (str[0]) {
strcat(str, ":");
addparam(params->exec_args, ¶ms->next_arg_index, str);
}
if (isDictionary(dict)) {
array = CFDictionaryGetValue(dict, kRASPropIPv4DestAddresses);
if (isArray(array)) {
nb = CFArrayGetCount(array);
for (i = 0; i < nb; i++) {
ipstr = CFArrayGetValueAtIndex(array, i);
if (isString(ipstr)) {
if (CFStringGetCString(ipstr, ipcstr, sizeof(ipcstr), kCFStringEncodingMacRoman)) {
if (ip = validate_ip_string(ipcstr, ip_addr, sizeof(ip_addr))) {
if (add_address(ip)) {
vpnlog(LOG_ERR, "Error while processing ip address %s\n", ip);
return -1;
}
} else
vpnlog(LOG_ERR, "Ignoring invalid ip address %s\n", ipcstr);
}
}
}
}
array = CFDictionaryGetValue(dict, kRASPropIPv4DestAddressRanges);
if (isArray(array)) {
if (CFArrayGetCount(array) % 2)
vpnlog(LOG_ERR, "Error - ip address ranges must be in pairs\n");
else {
nb = CFArrayGetCount(array);
for (i = 0; i < nb; i += 2) {
ipstr = CFArrayGetValueAtIndex(array, i);
ipstr2 = CFArrayGetValueAtIndex(array, i+1);
if (isString(ipstr) && isString(ipstr2)) {
if (CFStringGetCString(ipstr, ipcstr, sizeof(ipcstr), kCFStringEncodingMacRoman) &&
CFStringGetCString(ipstr2, ipcstr2, sizeof(ipcstr2), kCFStringEncodingMacRoman)) {
ip = validate_ip_string(ipcstr, ip_addr, sizeof(ip_addr));
ip2 = validate_ip_string(ipcstr2, ip_addr2, sizeof(ip_addr2));
if (ip && ip2) {
if (add_address_range(ip, ip2)) {
vpnlog(LOG_ERR, "Error while processing ip address range %s\n", ip);
return -1;
}
} else
vpnlog(LOG_ERR, "Ignoring invalid ip address range %s\n", ipcstr);
}
}
}
}
}
}
}
if (!address_avail()) {
vpnlog(LOG_ERR, "No valid client IP addresses\n");
return -1;
}
return 0;
}
static int process_ipv6_prefs(struct vpn_params *params)
{
CFDictionaryRef dict;
if ((dict = CFDictionaryGetValue(params->serverRef, kRASEntIPv6)) && isDictionary(dict)) {
addparam(params->exec_args, ¶ms->next_arg_index, "+ipv6");
addparam(params->exec_args, ¶ms->next_arg_index, "ipv6cp-use-persistent");
}
return 0;
}
static int process_dns_prefs(struct vpn_params *params)
{
CFPropertyListRef ref = 0;
CFDictionaryRef dict;
CFStringRef key = 0;
CFArrayRef array;
CFStringRef addr;
char str[OPT_STR_LEN];
int count, i;
if (isDictionary(CFDictionaryGetValue(params->serverRef, kRASEntIPv4))) {
dict = CFDictionaryGetValue(params->serverRef, kSCEntNetDNS);
if (isDictionary(dict)) {
array = CFDictionaryGetValue(dict, kRASPropDNSOfferedServerAddresses);
if (isArray(array)) {
count = CFArrayGetCount(array);
if (count == 0) { key = SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetDNS);
if (key) {
ref = SCDynamicStoreCopyValue(params->storeRef, key);
if (isDictionary(ref)) {
array = CFDictionaryGetValue(ref, kSCPropNetDNSServerAddresses);
if (isArray(array))
count = CFArrayGetCount(array);
}
CFRelease(key);
}
}
for (i = 0; i < count && i < 2; i++) {
addr = CFArrayGetValueAtIndex(array, i);
if (isString(addr)) {
str[0] = 0;
CFStringGetCString(addr, str, OPT_STR_LEN, kCFStringEncodingUTF8);
if (str[0])
addstrparam(params->exec_args, ¶ms->next_arg_index, "ms-dns", str);
}
}
if (ref)
CFRelease(ref);
}
}
}
return 0;
}
static int process_ppp_prefs(struct vpn_params *params)
{
char pathStr[MAXPATHLEN], optStr[OPT_STR_LEN];
u_int32_t len, lval, lval1, i;
CFDictionaryRef dict;
CFArrayRef array;
CFIndex count;
CFStringRef string;
int noCCP;
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPVerboseLogging, &lval, 0);
if (lval)
addparam(params->exec_args, ¶ms->next_arg_index, "debug");
get_str_option(params->serverRef, kRASEntPPP, kRASPropPPPLogfile, optStr, &len, "");
if (optStr[0]) {
sprintf(pathStr, "%s%s", optStr[0] == '/' ? "" : DIR_LOGS, optStr);
addstrparam(params->exec_args, ¶ms->next_arg_index, "logfile", pathStr);
}
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPDisconnectOnIdle, &lval, 0);
if (lval) {
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPDisconnectOnIdleTimer, &lval, OPT_COMM_IDLETIMER_DEF);
if (lval) {
addintparam(params->exec_args, ¶ms->next_arg_index, "idle", lval);
addparam(params->exec_args, ¶ms->next_arg_index, "noidlesend");
}
}
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPUseSessionTimer, &lval, 0);
if (lval) {
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPSessionTimer, &lval, OPT_COMM_IDLETIMER_DEF);
if (lval)
addintparam(params->exec_args, ¶ms->next_arg_index, "maxconnect", lval);
}
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPEchoEnabled, &lval, 0);
if (lval) {
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPEchoInterval, &lval, OPT_LCP_ECHOINTERVAL_DEF);
if (lval)
addintparam(params->exec_args, ¶ms->next_arg_index, "lcp-echo-interval", lval);
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPEchoFailure, &lval, OPT_LCP_ECHOINTERVAL_DEF);
if (lval)
addintparam(params->exec_args, ¶ms->next_arg_index, "lcp-echo-failure", lval);
}
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPCompressionACField, &lval, OPT_LCP_PCOMP_DEF);
if (lval == 0)
addparam(params->exec_args, ¶ms->next_arg_index, "noaccomp");
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPCompressionPField, &lval, OPT_LCP_ACCOMP_DEF);
if (lval == 0)
addparam(params->exec_args, ¶ms->next_arg_index, "nopcomp");
switch (params->server_subtype) {
case PPP_TYPE_PPPoE:
lval = OPT_LCP_MRU_PPPoE_DEF;
break;
case PPP_TYPE_PPTP:
lval = OPT_LCP_MRU_PPTP_DEF;
break;
case PPP_TYPE_L2TP:
lval = OPT_LCP_MRU_L2TP_DEF;
break;
default:
lval = OPT_LCP_MRU_DEF;
}
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPMRU, &lval, lval);
if (lval)
addintparam(params->exec_args, ¶ms->next_arg_index, "mru", lval);
switch (params->server_subtype) {
case PPP_TYPE_PPPoE:
lval = OPT_LCP_MTU_PPPoE_DEF;
break;
case PPP_TYPE_PPTP:
lval = OPT_LCP_MTU_PPTP_DEF;
break;
case PPP_TYPE_L2TP:
lval = OPT_LCP_MTU_L2TP_DEF;
break;
default:
lval = OPT_LCP_MTU_DEF;
}
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPMTU, &lval, lval);
if (lval)
addintparam(params->exec_args, ¶ms->next_arg_index, "mtu", lval);
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPReceiveACCM, &lval, OPT_LCP_RCACCM_DEF);
if (lval)
addintparam(params->exec_args, ¶ms->next_arg_index, "asyncmap", lval);
else
addparam(params->exec_args, ¶ms->next_arg_index, "receive-all");
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPLCPReceiveACCM, &lval, OPT_LCP_RCACCM_DEF);
if (lval) {
pathStr[0] = 0;
for (lval1 = 0; lval1 < 32; lval1++) {
if ((lval >> lval1) & 1) {
sprintf(optStr, "%d,", lval1);
strcat(pathStr, optStr);
}
}
pathStr[strlen(pathStr)-1] = 0; addstrparam(params->exec_args, ¶ms->next_arg_index, "escape", pathStr);
}
if (!(dict = CFDictionaryGetValue(params->serverRef, kRASEntIPv4))
|| !isDictionary(dict)) {
addparam(params->exec_args, ¶ms->next_arg_index, "noip");
}
else {
addintparam(params->exec_args, ¶ms->next_arg_index, "ip-src-address-filter", 1);
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPIPCPCompressionVJ, &lval, OPT_IPCP_HDRCOMP_DEF);
if (lval == 0)
addparam(params->exec_args, ¶ms->next_arg_index, "novj");
}
noCCP = 1;
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPCCPEnabled, &lval, 0);
if (lval) {
if ((dict = CFDictionaryGetValue(params->serverRef, kRASEntPPP)) != 0) {
array = CFDictionaryGetValue(dict, kRASPropPPPCCPProtocols);
if (isArray(array)) {
int mppe_found = 0;
count = CFArrayGetCount(array);
for (i = 0; i < count; i++) {
string = CFArrayGetValueAtIndex(array, i);
if (CFStringCompare(string, kRASValPPPCCPProtocolsMPPE, 0) == kCFCompareEqualTo) {
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPMPPEKeySize128, &lval, 0);
if (lval) {
mppe_found = 1;
addparam(params->exec_args, ¶ms->next_arg_index, "mppe-128");
}
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPMPPEKeySize40, &lval, 0);
if (lval) {
mppe_found = 1;
addparam(params->exec_args, ¶ms->next_arg_index, "mppe-40");
}
} else
vpnlog(LOG_ERR, "Invalid compression type specified - ignored\n");
}
if (mppe_found) {
noCCP = 0;
addparam(params->exec_args, ¶ms->next_arg_index, "mppe-stateless");
}
}
}
}
if (noCCP) addparam(params->exec_args, ¶ms->next_arg_index, "noccp");
get_int_option(params->serverRef, kRASEntPPP, kRASPropPPPACSPEnabled, &lval, 0);
if (lval == 0)
addparam(params->exec_args, ¶ms->next_arg_index, "noacsp");
dict = CFDictionaryGetValue(params->serverRef, kRASEntPPP);
if (isDictionary(dict)) {
array = CFDictionaryGetValue(dict, kRASPropPPPAuthenticatorProtocol);
if (isArray(array)) {
if ((count = CFArrayGetCount(array)) > 1) {
vpnlog(LOG_ERR, "%d authentication methods specified - only the first will be used\n", count);
}
string = CFArrayGetValueAtIndex(array, 0);
if (isString(string)) {
if (CFStringCompare(string, kRASValPPPAuthProtocolMSCHAP2, 0) == kCFCompareEqualTo)
addparam(params->exec_args, ¶ms->next_arg_index, "require-mschap-v2");
else if (CFStringCompare(string, kRASValPPPAuthProtocolMSCHAP1, 0) == kCFCompareEqualTo)
addparam(params->exec_args, ¶ms->next_arg_index, "require-mschap");
else if (CFStringCompare(string, kRASValPPPAuthProtocolCHAP, 0) == kCFCompareEqualTo)
addparam(params->exec_args, ¶ms->next_arg_index, "require-chap");
else if (CFStringCompare(string, kRASValPPPAuthProtocolPAP, 0) == kCFCompareEqualTo)
addparam(params->exec_args, ¶ms->next_arg_index, "require-pap");
else if (CFStringCompare(string, kRASValPPPAuthProtocolEAP, 0) == kCFCompareEqualTo) {
addparam(params->exec_args, ¶ms->next_arg_index, "require-eap");
int eapPluginFound = 0;
i = 0;
do {
lval = get_array_option(params->serverRef, kRASEntPPP, kRASPropPPPAuthenticatorEAPPlugins, i++, pathStr, &len, "");
if (pathStr[0]) {
strcat(pathStr, ".ppp"); if (!plugin_exists(pathStr)) {
vpnlog(LOG_ERR, "EAP plugin '%s' not found\n", pathStr);
return -1;
}
addstrparam(params->exec_args, ¶ms->next_arg_index, "eapplugin", pathStr);
eapPluginFound = 1;
}
}
while (lval);
if (!eapPluginFound) {
vpnlog(LOG_ERR, "No EAP authentication plugin(s) specified\n");
return -1;
}
} else {
vpnlog(LOG_ERR, "Unknown authentication type specified\n");
return -1;
}
}
}
}
i = 0;
do {
lval = get_array_option(params->serverRef, kRASEntPPP, kRASPropPPPAuthenticatorPlugins, i++, pathStr, &len, "");
if (pathStr[0]) {
strcat(pathStr, ".ppp"); if (!plugin_exists(pathStr)) {
vpnlog(LOG_ERR, "Authentication plugin '%s' not found\n", pathStr);
return -1;
}
addstrparam(params->exec_args, ¶ms->next_arg_index, "plugin", pathStr);
}
}
while (lval);
i = 0;
do {
lval = get_array_option(params->serverRef, kRASEntPPP, kRASPropPPPAuthenticatorACLPlugins, i++, pathStr, &len, "");
if (pathStr[0]) {
strcat(pathStr, ".ppp"); if (!plugin_exists(pathStr)) {
vpnlog(LOG_ERR, "Access Control plugin '%s' not found\n", pathStr);
return -1;
}
addstrparam(params->exec_args, ¶ms->next_arg_index, "plugin", pathStr);
}
}
while (lval);
i = 0;
do {
lval = get_array_option(params->serverRef, kRASEntPPP, kRASPropPPPPlugins, i++, pathStr, &len, "");
if (pathStr[0]) {
strcat(pathStr, ".ppp"); if (!plugin_exists(pathStr)) {
vpnlog(LOG_ERR, "Plugin '%s' not found\n", pathStr);
return -1;
}
addstrparam(params->exec_args, ¶ms->next_arg_index, "plugin", pathStr);
}
}
while (lval);
return 0;
}
int ppp_check_conflicts(struct vpn_params *params)
{
CFArrayRef array;
CFStringRef pattern, key;
CFStringRef type, subtype;
CFPropertyListRef ref;
int count, i, ret = 0;
char str[OPT_STR_LEN];
pattern = CFStringCreateWithFormat(0, 0, CFSTR("%@/%@/%@/%s/%@"), kSCDynamicStoreDomainState,
kSCCompNetwork, kRASRemoteAccessServer, ".*", kRASEntInterface);
if (pattern) {
if (array = SCDynamicStoreCopyKeyList(params->storeRef, pattern)) {
count = CFArrayGetCount(array);
for (i = 0; i < count; i++) {
key = CFArrayGetValueAtIndex(array, i);
if (key) {
ref = SCDynamicStoreCopyValue(params->storeRef, key);
if (isDictionary(ref)) {
type = CFDictionaryGetValue(ref, kRASPropInterfaceType);
subtype = CFDictionaryGetValue(ref, kRASPropInterfaceSubType);
if (isString(type) && isString(subtype) &&
(CFStringCompare(type, kRASValInterfaceTypePPP, 0) == kCFCompareEqualTo) &&
(CFStringCompare(subtype, params->serverSubTypeRef, 0) == kCFCompareEqualTo)) {
CFStringGetCString(subtype, str, OPT_STR_LEN, kCFStringEncodingMacRoman);
vpnlog(LOG_ERR, "Server for subtype %s already running - vpnd launch failed.\n", str);
ret = -1;
CFRelease(ref);
break;
}
CFRelease(ref);
}
}
}
CFRelease(array);
}
CFRelease(pattern);
}
return ret;
}
int ppp_kill_orphans(struct vpn_params* params)
{
CFArrayRef array;
CFStringRef pattern, key;
CFMutableStringRef mutable_key;
CFStringRef server_id;
CFNumberRef pidRef;
CFPropertyListRef interface_dict, ppp_dict;
int count, i, pid;
int ret = 0;
pattern = CFStringCreateWithFormat(0, 0, CFSTR("%@/%@/%@/%s/%@"), kSCDynamicStoreDomainState,
kSCCompNetwork, kSCCompService, ".*", kSCEntNetInterface);
if (pattern) {
if (array = SCDynamicStoreCopyKeyList(params->storeRef, pattern)) {
count = CFArrayGetCount(array);
for (i = 0; i < count; i++) {
key = CFArrayGetValueAtIndex(array, i);
if (key) {
interface_dict = SCDynamicStoreCopyValue(params->storeRef, key);
if (isDictionary(interface_dict)) {
server_id = CFDictionaryGetValue(interface_dict, CFSTR("ServerID"));
if (isString(server_id) && CFStringCompare(server_id, params->serverIDRef, 0) == kCFCompareEqualTo) {
mutable_key = CFStringCreateMutableCopy(0, 0, key);
if (CFStringFindAndReplace(mutable_key, kSCEntNetInterface, kSCEntNetPPP,
CFRangeMake(0, CFStringGetLength(mutable_key)), kCFCompareBackwards | kCFCompareAnchored)) {
ppp_dict = SCDynamicStoreCopyValue(params->storeRef, mutable_key);
CFRelease(mutable_key);
if (isDictionary(ppp_dict)) {
pidRef = CFDictionaryGetValue(ppp_dict, CFSTR("pid"));
if (isNumber(pidRef))
if (CFNumberGetValue(pidRef, kCFNumberIntType, &pid))
ret = kill(pid, SIGTERM);
}
if (ppp_dict)
CFRelease(ppp_dict);
}
}
}
if (interface_dict)
CFRelease(interface_dict);
}
}
CFRelease(array);
}
CFRelease(pattern);
}
return ret;
}