0

My app acts like a VPN client to an IPSec VPN server (strongSwan). I have used NEVPNManager to setup the VPN profile. The code is something like this:

#define KEY_PASSWORD @"password"
#define PASSWORD @"password"
#define VPN_SERVER @"10.1.1.1"
#define KEY_USERNAME @"username"
#define USERNAME @"myusername"
#define KEY_SHARED_SECRET @"sharedSecret"
#define SHARED_SECRET @"thisisthesecretekey"
#define LOCAL_IDENTIFIER @"myserver.com.client"
#define REMOTE_IDENTIFIER @"myserver.com.server"

-(void) setupVPNProfile {    
    NEVPNProtocolIKEv2 *protocol = [[NEVPNProtocolIKEv2 alloc] init];
    protocol.username = USERNAME;
    NSData *passwdRef = [self getData:KEY_PASSWORD];
    if (passwdRef == nil) {
        [self storeData:KEY_PASSWORD data:[PASSWORD dataUsingEncoding:NSUTF8StringEncoding]];
        passwdRef = [self getData:PASSWORD];
        NSLog(@"passwdRef: %@", [[NSString alloc] initWithData:passwdRef encoding:NSUTF8StringEncoding]);
    }
    protocol.passwordReference = passwdRef;

    protocol.serverAddress = VPN_SERVER;

    protocol.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
    NSData *sharedSecretRef = [self getData:KEY_SHARED_SECRET];
    if (sharedSecretRef == nil) {
        [self storeData:KEY_SHARED_SECRET data:[SHARED_SECRET dataUsingEncoding:NSUTF8StringEncoding]];
        sharedSecretRef = [self getData:KEY_SHARED_SECRET];
        NSLog(@"sharedSecretRef: %@", [[NSString alloc] initWithData:sharedSecretRef encoding:NSUTF8StringEncoding]);

    }
    protocol.sharedSecretReference = sharedSecretRef;

    protocol.localIdentifier = LOCAL_IDENTIFIER;
    protocol.remoteIdentifier = REMOTE_IDENTIFIER;

    protocol.useExtendedAuthentication = YES;

    protocol.disconnectOnSleep = NO;

    self.manager.protocolConfiguration = protocol;

    self.manager.enabled = YES;
}

#pragma mark - Keychain methods

- (void) storeData: (NSString * )key data:(NSData *)data {
    NSLog(@"Store Data");
    NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
    [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
    [dict setObject:@"VPN" forKey:(__bridge id)kSecAttrService];
    [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
    [dict setObject:data forKey:(__bridge id)kSecValueData];

    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
    if(errSecSuccess != status) {
        NSLog(@"Unable add item with key =%@ error:%d",key,(int)status);
    }
}

- (NSData *) getData: (NSString *)key {
    NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
    [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
    [dict setObject:@"VPN" forKey:(__bridge id)kSecAttrService];
    [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
    [dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
    [dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnPersistentRef];

    CFTypeRef result = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);

    if( status != errSecSuccess) {
        NSLog(@"Unable to fetch item for key %@ with error:%d",key,(int)status);
        return nil;
    }

    NSData *resultData = (__bridge NSData *)result;
   return resultData;
}

- (BOOL) removeData: (NSString *) key {
    NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
    [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
    [dict setObject:@"VPN" forKey:(__bridge id)kSecAttrService];
    [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];

    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dict);

    if( status != errSecSuccess) {
        NSLog(@"Unable to fetch item for key %@ with error:%d",key,(int)status);
        return NO;
    }

    return YES;
}

Here values like username, password server-address are stored in MACROS defined above. Per the required design, password and sharedSecret is to be stored as Persistent KeyChain items. In this setup, how can I gracefully change values like username, password, serverAddress, sharedSecret?

What I have tried so far:

  1. I changed the values like server address, username in the code. I used storeData functions to overwrite the password and sharedSecret persistent keychain items. However when I ran the app after making these changes I wasn't able to connect to new VPN server. Please note that I didn't make any mistake with the new values I had to use. I double checked before updating. Please also note that I was able to connect to VPN server when I created a .mobileconfig file out of those new params. Its just that my VPN client app wasn't able to connect to server anymore. It would try connecting and would disconnect again. Uninstalling the app had no effect as well.

  2. Setting manager.protocolConfiguration = nil. Doing this has no effect. The installed protocolConfiguration stays intact.

I do not want to remove the VPN profile by calling manager removePreferencesWithCompletionHandler:] since the user would see the VPN related pop-up again when he tries to connect to VPN server. If anyone has done this before please help. Thanks.

Varun Singh
  • 1,135
  • 13
  • 18

1 Answers1

0

To change VPN account you should be able to simply change the VPN account parameters and then start VPNManager with the new values. You don't need to stop the currently running VPN Session and you certainly don't need to delete the profile, just call NEVPNManager.shared.locadFromPreferences, make the changes, then .saveToPreferences and startVPNTunnel as normal.

Since you're not able to get VPN tunnel to start even with the known good credentials using variables instead of macros, then you are probably passing values incorrectly from the keychain or not what you expect. I would compare every parameter using print statements, first with your current working solution that uses macros then with the non-working keychain code.

hungri-yeti
  • 584
  • 1
  • 5
  • 6
  • Thanks. I will try this and will let you know what happens. Looks like I will have to at least stop the current connection. Only then I can a call to startVPNTunnel would make sense. However, I can live with that. – Varun Singh Mar 27 '17 at 10:13