Soheil
Soheil

Reputation: 5354

Creating PPTP VPN on Mac OS X Programatically

I'm trying to create an app that setup VPN on Mac programatically. Unfortunately Apple doc on networking area are not very specific on setting up and connecting to VPN.

So I found some sample codes online for setting up L2TP and IPsec but I couldn't find anything for PPTP. Below is my general code that I'm using, but PPTP does not work for me. When I run it, it sure will crash.

So below is some of the questions that I just can't figure them out.

-What am I missing for setting up PPTP VPN ?

-How do I connect to setup VPN ?

-How do I find already added VPN and delete them ?

- (AuthorizationRef) getAuth {
    AuthorizationRef auth = NULL;
    OSStatus status;
    status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, [self flags], &auth);
    if (status == errAuthorizationSuccess) {
        //NSLog(@"Successfully obtained Authorization reference");
    } else {
        NSLog(@"Could not obtain Authorization reference");
        exit(101);   
    }
    return auth;    
}

- (AuthorizationFlags) flags {
    return kAuthorizationFlagDefaults           |
    kAuthorizationFlagExtendRights       |
    kAuthorizationFlagInteractionAllowed |
    kAuthorizationFlagPreAuthorize;   
}
- (void)setupVPN:(VPNConfig *)vpnConfig {
    // Obtaining permission to modify network settings
    AuthorizationRef auth = [self getAuth];
    SCPreferencesRef prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("macVPN"), NULL, auth);
    // Making sure other process cannot make configuration modifications
    // by obtaining a system-wide lock over the system preferences.
    if (SCPreferencesLock(prefs, TRUE)) {
        NSLog(@"Gained superhuman rights.");
    } else {
        NSLog(@"Sorry, without superuser privileges I won't be able to add any VPN interfaces.");
        return;   
    }
    // If everything will work out fin
    [self createService:vpnConfig usingPreferencesRef:prefs];

    // We're done, other processes may modify the system configuration again
    SCPreferencesUnlock(prefs);
    return;    
}

#pragma mark - Create VPN Service
// This method creates one VPN interface according to the desired configuration
- (void) createService:(VPNConfig*)config usingPreferencesRef:(SCPreferencesRef)prefs {
    NSLog(@"Creating new %@ Service using %@", config.humanType, config);
    // These variables will hold references to our new interfaces

    SCNetworkInterfaceRef bottomInterface = SCNetworkInterfaceCreateWithInterface(kSCNetworkInterfaceIPv4,kSCNetworkInterfaceTypePPTP);

    SCNetworkInterfaceRef topInterface = SCNetworkInterfaceCreateWithInterface(bottomInterface, kSCNetworkInterfaceTypePPP);

    // Creating a new, fresh VPN service in memory using the interface we already created

    SCNetworkServiceRef service = SCNetworkServiceCreate(prefs, topInterface);
    // That service is to have a name
    SCNetworkServiceSetName(service, (__bridge CFStringRef)config.name);
    // And we also woould like to know the internal ID of this service

    NSString *serviceID = (__bridge NSString *)(SCNetworkServiceGetServiceID(service));
    // It will be used to find the correct passwords in the system keychain
    config.serviceID = serviceID;

    // Interestingly enough, the interface variables in itself are now worthless
    // We used them to create the service and that's it, we cannot modify them any more.
    CFRelease(topInterface);
    CFRelease(bottomInterface);
    topInterface = NULL;
    bottomInterface = NULL;

    topInterface = SCNetworkServiceGetInterface(service);

    // Let's apply all configuration to the PPTP interface
    // Specifically, the servername, account username and password
    if (SCNetworkInterfaceSetConfiguration(topInterface, config.PPTPConfig)) {
        NSLog(@"Successfully configured PPP interface of service %@", config.name);
    } else {
        NSLog(@"Error: Could not configure PPP interface for service %@", config.name);
        return;    
    }

    if (SCNetworkInterfaceSetExtendedConfiguration(topInterface, NULL, config.PPTPConfig)) {
        NSLog(@"Successfully configured IPSec on PPP interface for service %@", config.name);
    } else {        
        NSLog(@"Error: Could not configure PPTP on PPP interface for service %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());

        return;
    }
    NSLog(@"Adding default protocols (DNS, etc.) to service %@...", config.name);

    if (!SCNetworkServiceEstablishDefaultConfiguration(service)) {
        NSLog(@"Error: Could not establish a default service configuration for %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
        return;    
    }

    NSLog(@"Fetching set of all available network services...");    
    SCNetworkSetRef networkSet = SCNetworkSetCopyCurrent(prefs);

    if (!networkSet) {
        NSLog(@"Error: Could not fetch current network set when creating %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
        return;   
    }
    if (!SCNetworkSetAddService (networkSet, service)) {
        if (SCError() == 1005) {
            NSLog(@"Skipping VPN Service %@ because it already exists.", config.humanType);
            return;
        } else {
            NSLog(@"Error: Could not add new VPN service %@ to current network set. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());
            return;   
        }    
    }

    NSLog(@"Fetching IPv4 protocol of service %@...", config.name);

    SCNetworkProtocolRef protocol = SCNetworkServiceCopyProtocol(service, kSCNetworkProtocolTypeIPv4);



    if (!protocol) {

        NSLog(@"Error: Could not fetch IPv4 protocol of %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());

        return;

    }



    NSLog(@"Configuring IPv4 protocol of service %@...", config.name);

    if (!SCNetworkProtocolSetConfiguration(protocol, config.L2TPIPv4Config)) {

        NSLog(@"Error: Could not configure IPv4 protocol of %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());

        return;

    }



    NSLog(@"Commiting all changes including service %@...", config.name);

    if (!SCPreferencesCommitChanges(prefs)) {

        NSLog(@"Error: Could not commit preferences with service %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());

        return;

    }



    NSLog(@"Preparing to add Keychain items for service %@...", config.name);





    // The password and the shared secret are not stored directly in the System Preferences .plist file

    // Instead we put them into the KeyChain. I know we're creating new items each time you run this application

    // But this actually is the same behaviour you get using the official System Preferences Network Pane

    [VPNKeychain createPasswordKeyChainItem:config.name forService:serviceID withAccount:config.username andPassword:config.password];

    [VPNKeychain createSharedSecretKeyChainItem:config.name forService:serviceID withPassword:config.sharedSecret];





    if (!SCPreferencesApplyChanges(prefs)) {

        NSLog(@"Error: Could not apply changes with service %@. %s (Code %i)", config.name, SCErrorString(SCError()), SCError());

        return;

    }



    NSLog(@"Successfully created %@ VPN %@ with ID %@", config.humanType, config.name, serviceID);
    return;
}

And this is my VPNConfig file :

// VPNConfig.h
@interface VPNConfig : NSObject

@property (atomic) NSUInteger type;
@property (strong) NSString *serviceID;
@property (strong) NSString *name;
@property (strong) NSString *endpointPrefix;
@property (nonatomic) NSString *endpoint;
@property (strong) NSString *endpointSuffix;
@property (strong) NSString *username;
@property (strong) NSString *password;
@property (strong) NSString *sharedSecret;
@property (readonly) NSString *humanType;

@property (readonly) CFDictionaryRef PPTPConfig;
@property (readonly) CFDictionaryRef L2TPPPPConfig;
@property (readonly) CFDictionaryRef L2TPIPSecConfig;
@property (readonly) CFDictionaryRef L2TPIPv4Config;

@end



// VPNConfig.m

typedef NS_ENUM(NSInteger, VPNServiceType) {

    VPNServicePPTP ,

    VPNServiceL2TPOverIPSec,

    VPNServiceCiscoIPSec

};


@implementation VPNConfig

@synthesize type, name, endpointPrefix, endpointSuffix, username, password, sharedSecret;

@synthesize endpoint = _endpoint;



- (void) setEndpoint:(NSString *)newEndpoint {

    _endpoint = newEndpoint;

}



- (NSString*) endpoint {

    if (_endpoint) return _endpoint;

    if ((!endpointPrefix && !endpointSuffix) || ([endpointPrefix isEqualToString:@""] && [endpointSuffix isEqualToString:@""])) return NULL;

    return [NSString stringWithFormat:@"%@%@", endpointPrefix, endpointSuffix];

}



- (BOOL) is:(NSUInteger)aType {

    return self.type == aType;

}



- (NSString*) humanType {

    switch(self.type) {

        case VPNServicePPTP          : return @"PPTP"; break;

        case VPNServiceL2TPOverIPSec : return @"L2TP over IPSec"; break;

        case VPNServiceCiscoIPSec    : return @"Cisco IPSec"; break;

        default                      : return @"Unknown"; break;

    }

}



- (NSString*) description {

    return [NSString stringWithFormat:@"<[%@] name=%@ endpointPrefix=%@ endpoint=%@ endpointSuffix=%@ username=%@ password=%@ sharedSecret=%@>", self.humanType, self.name, self.endpointPrefix, self.endpoint, self.endpointSuffix, self.username, self.password, self.sharedSecret];

}



- (CFDictionaryRef) PPTPConfig {

    CFStringRef keys[4] = { NULL, NULL, NULL, NULL };

    CFStringRef vals[4] = { NULL, NULL, NULL, NULL };

    CFIndex count = 0;



    keys[count] = kSCPropNetPPPAuthName;

    vals[count++] = (__bridge CFStringRef)self.username;



    keys[count] = kSCPropNetPPPAuthPassword;

    vals[count++] = (__bridge CFStringRef)self.serviceID;



    keys[count] = kSCPropNetPPPCommRemoteAddress;

    vals[count++] = (__bridge CFStringRef)self.endpoint;



    keys[count]    = kSCPropUserDefinedName;

    vals[count++]  = (__bridge CFStringRef)C_VPN_NAME;



    return CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

}


- (CFDictionaryRef) L2TPIPSecConfig {

    CFStringRef keys[3] = { NULL, NULL, NULL };

    CFStringRef vals[3] = { NULL, NULL, NULL };

    CFIndex count = 0;



    keys[count] = kSCPropNetIPSecAuthenticationMethod;

    vals[count++] = kSCValNetIPSecAuthenticationMethodSharedSecret;



    keys[count] = kSCPropNetIPSecSharedSecretEncryption;

    vals[count++] = kSCValNetIPSecSharedSecretEncryptionKeychain;



    keys[count] = kSCPropNetIPSecSharedSecret;

    vals[count++] = (__bridge CFStringRef)[NSString stringWithFormat:@"%@.SS", self.serviceID];



    return CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

}



- (CFDictionaryRef) L2TPIPv4Config {

    CFStringRef keys[5] = { NULL, NULL, NULL, NULL, NULL };

    CFStringRef vals[5] = { NULL, NULL, NULL, NULL, NULL };

    CFIndex count = 0;



    keys[count] = kSCPropNetIPv4ConfigMethod;

    vals[count++] = kSCValNetIPv4ConfigMethodPPP;



    int one = 1;

    keys[count] = kSCPropNetOverridePrimary;
    vals[count++] = CFNumberCreate(NULL, kCFNumberIntType, &one);

    return CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}

Upvotes: 2

Views: 1966

Answers (1)

Malav Soni
Malav Soni

Reputation: 2848

you can check out below link for vpn/pptp connection

http://lists.apple.com/archives/macnetworkprog/2011/Jul/msg00001.html

Hope this can solve your problem.

Upvotes: 1

Related Questions