Profil VPN iOS : passwordReference, pas le mot de passe
Une référence de trousseau est une poignée, pas le secret. Transmettre des octets explique l’erreur -50, l’échec au redémarrage et le piège de la rotation dans les profils VPN enregistrés.
Vous installez un profil, le tunnel monte et tout semble terminé. Puis le téléphone redémarre, on-demand réveille la connexion sans application au premier plan, et le tunnel refuse de démarrer. Le profil est toujours là et le mot de passe toujours correct, et c’est pourquoi cette panne envoie chercher au mauvais endroit. Une Network Extension ne peut pas lire votre secret depuis le profil : il lui faut une référence de trousseau qu’elle puisse résoudre elle-même.
Le parcours ci-dessous suit une seule tentative de connexion à travers ce passage de relais, de la requête qui construit la référence au redémarrage qui la met à l’épreuve.
Le profil stocke une poignée, pas le secret
NEVPNProtocol expose passwordReference et sharedSecretReference pour une raison. Quand le démon démarre un tunnel, il ne tourne peut-être pas dans votre processus, donc il ne peut pas appeler votre code et demander un mot de passe. Il lit une référence de trousseau persistante, la résout dans son propre contexte et récupère l’élément lui-même.
Vous construisez cette référence en la demandant au trousseau, ce qui est une requête différente de celle qui demande les données :
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "vpn-credentials",
kSecAttrAccount as String: "user",
kSecReturnPersistentRef as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]Remettez l’objet renvoyé au protocole, pas les données de l’élément. En pratique le profil enregistre où vit le secret, et c’est aussi pourquoi une sauvegarde de la configuration ne duplique pas discrètement le mot de passe dans un plist.
Pourquoi l’API répond -50
errSecParam signifie que la requête ou la valeur était mal formée pour l’opération, et c’est l’erreur que l’on voit le plus souvent quand les requêtes de référence et de données se confondent. Demander des données alors que le code récepteur attend une référence, affecter des octets bruts à passwordReference ou interroger avec un groupe d’accès que l’application ne peut pas utiliser, toutes la produisent.
Les bibliothèques d’enveloppe y invitent. Un utilitaire qui renvoie des octets n’est pas interchangeable avec un autre qui renvoie une référence, donc la réparation est généralement une autre requête plutôt qu’un autre mot de passe.
La même inadéquation explique un profil qui s’enregistre sans broncher puis ne se connecte pas : ce qui a été stocké est un blob, pas une poignée, et le démon n’a rien à résoudre.
S’enregistre bien, échoue à la connexion
L’enregistrement exerce le contexte de votre application. La connexion exerce celui du démon, et les deux n’ont pas le même accès. Un profil peut donc passer tous les tests du simulateur et échouer au premier démarrage on-demand.
C’est aussi la forme du symptôme au redémarrage : cela marche tant que l’application est ouverte et meurt quand le système démarre le tunnel tout seul.
Testez délibérément le chemin d’arrière-plan avant de livrer. Déclenchez une connexion on-demand application fermée, puis lisez la console de l’appareil plutôt que le journal de l’application, car la panne est enregistrée par le processus qui a tenté de résoudre la référence. Si la console montre que le démon rejette l’élément, la référence est bonne et le problème vient de l’accessibilité de l’élément.
L’accessibilité décide si un redémarrage fonctionne
L’élément doit être lisible par le processus système qui lance le tunnel, et c’est un public plus large que votre application au premier plan. Cette contrainte choisit l’attribut d’accessibilité à votre place. Un élément protégé jusqu’au premier déverrouillage est disponible pour un démarrage en arrière-plan après que l’appareil a été déverrouillé une fois ; un élément qui exige que l’appareil soit déverrouillé maintenant ne l’est pas.
Choisir la classe la plus stricte est tentant et c’est exactement ce qui casse un profil on-demand sur un téléphone verrouillé.
Après avoir changé de classe, recréez l’élément au lieu de modifier les attributs de l’existant, puis réenregistrez le protocole avec une référence neuve. Les changements d’attributs sur un élément existant ne produisent pas toujours le comportement que la documentation laisse entendre, et un élément reconstruit lève le doute.
La rotation est là où les profils enregistrés pourrissent
Changer le mot de passe dans le trousseau ne change pas automatiquement ce que le profil enregistré désigne. Supprimer l’élément est pire : le profil garde une référence vers quelque chose qui n’existe plus, et la panne ressemble à un refus d’authentification.
Effectuez la rotation dans un seul ordre. Mettez l’élément à jour, interrogez une nouvelle référence persistante, réenregistrez le protocole, puis connectez-vous application fermée pour prouver que le démon peut encore la résoudre. Gardez le mot de passe utilisateur et le secret partagé dans des éléments séparés afin que chacun tourne selon son propre calendrier, et ne journalisez jamais le dictionnaire de requête ni la référence elle-même. La poignée n’est pas un secret, mais c’est une carte vers un.