Quantcast
Channel: Recent Threads — Xamarin Community Forums
Viewing all articles
Browse latest Browse all 204402

How to get a persistent reference to a keychain item for VPN usage?

$
0
0

Hello,

I'm currently trying to establish a custom VPN connection via the Network Extension Library (NEVpnManager).
After reading and trying a lot, it works like a charm with one big issue I can't get fixed:
The NEVpnProtocol can receive a reference to a password that is stored in the apps keychain so that the user doesn't have to enter the password every time he logs in. No matter what I do, the user has to enter a password, or I can't save the NEVpnManager Settings.
As you can see here NEVpnProtocol.PasswordReference needs a persistent reference to work, otherwise it won't work.
Sadly I don't completely understand the objectC Code behind this link, that's probably why I can't get this to work with Xamarin.

In every Keychain example for Xamarin I found SecKeyChain.QueryAsRecord is used, however I found out that there's also the SecKeyChain.QueryAsData method, which can receive a parameter "wantPersistentReference". That should probably do the trick, but when I use this method I can't save the NEVpnManager settings:

Error Domain=NEVPNErrorDomain Code=5 "IPC failed" UserInfo={NSLocalizedDescription=IPC failed}

IPC probably means Inter Process Communication, allowing different processes to communicate, so I think my problem is that the NEVpnManager can't read the password because it is "persistent".

Is it possible to store and receive the log-in details in a way so that this works like intended ( = user won't be asked for a password)?
Any help is appreaciated, thank you in advance.

The sources I used to create this code:
Establish VPN Connection:
http://ramezanpour.net/post/2014/08/03/configure-and-manage-vpn-connections-programmatically-in-ios-8/
Apple KeyChain Concept:
https://developer.apple.com/library/mac/documentation/Security/Conceptual/keychainServConcepts/02concepts/concepts.html
Xamarin KeyChain Example:
https://github.com/xamarin/monotouch-samples/tree/master/Keychain
Persistent KeyChain iOS:
http://ramezanpour.net/post/2014/09/26/how-to-get-persistent-references-to-keychain-items-in-ios/

Finally, here my complete code, with the code not working ("IPC failed error") and the code working but with a password prompt (in comments).
Please note that in this stage of the coding process the user can't choose a username / password.
Usage of the code:
1) Create VPNServiceIOS object
2) SaveVPNConfig
3) StartVPNConnection
4) StopVPNConnection()

public class VPNServiceIOS : IVPNService
    {
        private NEVpnManager manager;


    public VPNServiceIOS ()
    {
        manager = NEVpnManager.SharedManager;

        var s = new SecRecord (SecKind.GenericPassword) {
            Label = "VPNTest",
            Description = "Item description",
            Account = "MYACCOUNTNAME",
            Service = "VPNService",
            Comment = "Your comment here",
            ValueData = NSData.FromString ("MYPASSWORD", NSStringEncoding.UTF8),
            Generic = NSData.FromString ("VPNPas", NSStringEncoding.UTF8),
        };

        var err = SecKeyChain.Add (s);
        if (err != SecStatusCode.Success && err != SecStatusCode.DuplicateItem) {
            Console.WriteLine ("Password not in Keychain:");
            Console.WriteLine (err.ToString ());
        }
    }

    public void StartVPNConnection ()
    {
        manager.LoadFromPreferences (error => {
            if (error != null) {
                Console.WriteLine ("Cant load VPN settings: ");
                Console.WriteLine (error.ToString ());
            }
        });
        NSError error2 = new NSError ();
        manager.Connection.StartVpnTunnel (out error2);
        if (error2 != null) {
            Console.WriteLine ("Cant establish connection:");
            Console.WriteLine (error2.ToString ());
        }
    }

    public void StopVPNConnection ()
    {
        manager.Connection.StopVpnTunnel ();
    }

    public void SaveVPNConfig (string type, string description, string server, string remoteIdentifier, string localIdentifier, string username)
    {
        manager.LoadFromPreferences (error => {
            if (error != null) {
                Console.WriteLine ("Cant load VPN Settings: ");
                Console.WriteLine (error);
            } else {
                NEVpnProtocol p = null;
                switch (type) {
                case "IKEv2":
                    NEVpnProtocolIke2 ike2 = new NEVpnProtocolIke2 ();
                    ike2.AuthenticationMethod = NEVpnIkeAuthenticationMethod.None;
                    ike2.LocalIdentifier = localIdentifier;
                    ike2.RemoteIdentifier = remoteIdentifier;
                    ike2.UseExtendedAuthentication = true;
                    ike2.DisconnectOnSleep = false;
                    p = ike2;
                    break;
                default:
                    Console.WriteLine ("unknown Protocol!");
                    break;
                }
                p.Username = username;
                p.ServerAddress = server;

                manager.LocalizedDescription = description;

                var s = new SecRecord (SecKind.GenericPassword) {
                    Account = "MYACCOUNTNAME",
                    Generic = NSData.FromString ("VPNPas", NSStringEncoding.UTF8)
                };

// CODE NOT WORKING:
                    SecStatusCode res;
                    var match = SecKeyChain.QueryAsData(s, true, out res);
                    if (res == SecStatusCode.Success){
                        p.PasswordReference = match;
                    }
                    else {
                        Console.WriteLine (res);
                    }

// CODE WORKING but only with password prompt:
//                    SecStatusCode res;
//                    var match = SecKeyChain.QueryAsRecord(s, out res);
//                    if (res == SecStatusCode.Success){
//                        p.PasswordReference = match.ValueData;
//                    }
//                    else {
//                        Console.WriteLine (res);
//                    }

                manager.ProtocolConfiguration = p;
                manager.OnDemandEnabled = false;

                manager.SaveToPreferences (error2 => {
                    if (error2 != null) {
                        Console.WriteLine ("Cant save manager settings:");
                        Console.WriteLine (error2.Code);
                        Console.WriteLine (error2.DebugDescription);
                        Console.WriteLine (error2.Description);
                        Console.WriteLine (error2.Domain);
                        Console.WriteLine (error2.HelpAnchor);
                        Console.WriteLine (error2.LocalizedDescription);
                        Console.WriteLine (error2.LocalizedFailureReason);
                        Console.WriteLine (error2.LocalizedRecoverySuggestion);
                    }
                });
            }
        });
    }

    public void DeleteVPNConfig ()
    {
        manager.RemoveFromPreferences ((NSError error) => {
            if (error != null) {
                Console.WriteLine ("Cant delete VPN Config:");
                Console.WriteLine (error.ToString ());
            } else {
                isConnected = false;
            }
        });
    }
}

Viewing all articles
Browse latest Browse all 204402

Trending Articles