Can't create IPSEC connection using NEVPNManager on iOS
Asked Answered
H

1

10

I'm trying to create an IPSEC VPN connection in my iOS app.

My code for setting up the configuration looks like this:

-(void)setUpConfig
   NEVPNManager *manager = [NEVPNManager sharedManager];

    int status = manager.connection.status;

    if (status == NEVPNStatusConnected) {
        manager.connection stopVPNTunnel];
    } else {
        [manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
            NSError *startError;

        if (error) {
            NSLog(@"Load config failed [%@]", error.localizedDescription);
            return;
        }

        NEVPNProtocolIPSec *p = (NEVPNProtocolIPSec *)self.manager.protocol;
        if (!p) {
            p = [[NEVPNProtocolIPSec alloc] init];
        }

        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"base64_encoded_cert" ofType:@"txt"];
        NSString *certBase64String = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
        NSString *certPassword = @"cert_import_password";

        NSString *vpnUsername = @"myUsername";
        NSString *vpnPassword = @"myPassword";
        NSString *url = @"my.server.address";

        // This saves my credentials to the keychain and returns a persistent keychain reference
        NSData *passRef = [self addVPNCredentialsToKeychain:vpnUsername withPassword:vpnPassword];

        p.username = vpnUsername;
        p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
        p.serverAddress = url;
        p.passwordReference = passRef;
        p.identityData = [NSData dataWithBase64EncodedString:certBase64String];
        p.identityDataPassword = certPassword;
        p.disconnectOnSleep = NO;
        p.useExtendedAuthentication = YES;

        [manager setProtocol:p];
        [manager setOnDemandEnabled:NO];
        [manager setLocalizedDescription:@"My VPN"];

        [manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
            if(error) {
                NSLog(@"Save error: %@", error);
            } else {
                NSLog(@"Saved!");
                }
            }];
        }];
    }
}


-(NSData*)addVPNCredentialsToKeychain:(NSString*)username withPassword:(NSString*)password
{
    NSMutableDictionary *keychainItem = [NSMutableDictionary dictionary];

    NSData *encodedIdentifier = [username dataUsingEncoding:NSUTF8StringEncoding];

    keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
    keychainItem[(__bridge id)kSecAttrDescription] = @"A password used to authenticate on a VPN server";
    keychainItem[(__bridge id)kSecAttrGeneric] = encodedIdentifier;
    keychainItem[(__bridge id)kSecAttrAccount] = encodedIdentifier;
    keychainItem[(__bridge id)kSecAttrService] = [[NSBundle mainBundle] bundleIdentifier];
    keychainItem[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
    keychainItem[(__bridge id)kSecReturnPersistentRef] = @YES;

    CFTypeRef typeResult = nil;

    OSStatus sts = SecItemCopyMatching((__bridge CFDictionaryRef)keychainItem, &typeResult);

    NSLog(@"Error Code: %d", (int)sts);

    if(sts == noErr) {
        NSData *theReference = (__bridge NSData *)typeResult;
    return theReference;

    } else {
        keychainItem[(__bridge id)kSecValueData] = [password dataUsingEncoding:NSUTF8StringEncoding]; //Our password

        OSStatus sts = SecItemAdd((__bridge CFDictionaryRef)keychainItem, &typeResult);
        NSLog(@"Error Code: %d", (int)sts);

        NSData *theReference = (__bridge NSData *)(typeResult);
        return theReference;
    }
    return nil;
}

I then attempt to open a VPN connection like so:

-(void)connect
    NEVPNManager *manager = [NEVPNManager sharedManager];
    [manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
        NSError *startError;
        [manager.connection startVPNTunnelAndReturnError:&startError];

        if(startError) {
            NSLog(@"Start error: %@", startError.localizedDescription);
        }
    }];
}

And this does work under most conditions. The problem I am experiencing is that after factory restoring a device (on iOS 8, of course), and attempting to go through this setup and connection, my profile installs just fine, but the VPN fails to connect. In fact, my interpretation is that it is failing to attempt to connect.

After factory restoring a device and attempting to connect using my method, the following shows up in the device logs:

<Notice>: NESMLegacySession[MyVPN:BB73C098-B22E-46D3-9491-2A6D9F559F8F]: Received a start command from VPNApp[256], but start was rejected

Going into the Settings app and attempting to manually toggle the VPN using the switch under "Bluetooth" results in the switch turning on for a split second and then immediately going by to off. In this case, the following log is produced:

<Warning>: -[VPNBundleController _vpnNetworkingIsDisabled]: Airplane mode: 0, WiFi Enabled: 1

In both cases, no error dialog is produced when the VPN fails to start connecting - just the logs.

I can get around this problem by navigating to Settings > General > VPN. Once having just gone to that page (i.e. Not toggling VPN there), I can then control VPN just fine. Even going to that page before a VPN configuration is even installed results in me being able to connect just fine after installing a configuration.

My goal is to be able to start the VPN connection without having to first go to that VPN page in Settings. Can anyone shed some light on the situation? It seems to me like I'm missing something to first enable VPN connections.

Hymenium answered 19/10, 2014 at 17:1 Comment(0)
S
6

This appears because VPN configuration stayed disabled by default for initial VPN connection.

You must enable VPN before saveToPreferencesWithCompletionHandler.

[[NEVPNManager sharedManager] setEnabled:YES];

Example:

[[NEVPNManager sharedManager] loadFromPreferencesWithCompletionHandler: ^(NSError *error) {
        if (error) {
            NSLog(@"Load error: %@", error);
        }
        else {
            // No errors! The rest of your codes goes here...
            NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
            p.serverAddress = @"VPN SERVER ADDRESS";
            p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
            p.localIdentifier = @"Local identifier";
            p.remoteIdentifier = @"Remote identifier";
            p.useExtendedAuthentication = YES;
            p.identityData = [NSData dataWithBase64EncodedString:certBase64String];;
            p.identityDataPassword = @"identity password";
            p.disconnectOnSleep = NO;
            
            // Set protocol
            [[NEVPNManager sharedManager] setProtocol:p];

            // Set on demand
            NSMutableArray *rules = [[NSMutableArray alloc] init];
            NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new];
            [rules addObject:connectRule];
            [[NEVPNManager sharedManager] setOnDemandRules:rules];
            
            // Set localized description
            [[NEVPNManager sharedManager] setLocalizedDescription:@"Description"];
            
            // Enable VPN
            [[NEVPNManager sharedManager] setEnabled:YES];
            
            // Save to preference
            [[NEVPNManager sharedManager] saveToPreferencesWithCompletionHandler: ^(NSError *error) {
                NSLog(@"Save VPN to preference complete");
                if (error) {
                    NSLog(@"Save to preference error: %@", error);
                   
                }
            }];
        }
    }];
Sorghum answered 27/10, 2014 at 21:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.