iOS-VPN-Profil: passwordReference statt Passwort
Eine Keychain-Referenz ist ein Griff, nicht das Geheimnis. Bytes zu übergeben erklärt Fehler -50, den Neustart-Fehler und die Rotationsfalle in gespeicherten VPN-Profilen.
Sie installieren ein Profil, der Tunnel kommt hoch, und alles sieht fertig aus. Dann startet das Telefon neu, on-demand weckt die Verbindung ohne App im Vordergrund, und der Tunnel startet nicht. Das Profil ist noch da, das Passwort ist noch richtig, und genau deshalb schickt dieser Fehler Leute an die falsche Stelle. Eine Network Extension kann Ihr Geheimnis nicht aus dem Profil lesen: Sie braucht eine Keychain-Referenz, die sie selbst auflösen kann.
Der folgende Durchlauf verfolgt einen Verbindungsversuch durch diese Übergabe, von der Abfrage, die die Referenz baut, bis zum Neustart, der sie prüft.
Das Profil speichert einen Griff, nicht das Geheimnis
NEVPNProtocol legt passwordReference und sharedSecretReference aus gutem Grund offen. Wenn der Daemon einen Tunnel startet, läuft er womöglich nicht in Ihrem Prozess, also kann er Ihren Code nicht aufrufen und nach einem Passwort fragen. Er liest eine dauerhafte Keychain-Referenz, löst sie in seinem eigenen Kontext auf und holt den Eintrag selbst.
Sie bauen diese Referenz, indem Sie die Keychain danach fragen, und das ist eine andere Anfrage als die nach den Daten:
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
]Übergeben Sie das zurückgegebene Objekt an das Protokoll, nicht die Daten des Eintrags. Praktisch hält das Profil fest, wo das Geheimnis liegt, und deshalb dupliziert eine Sicherung der Konfiguration das Passwort nicht stillschweigend in eine plist.
Warum die API mit -50 antwortet
errSecParam bedeutet, dass die Anfrage oder der Wert für die Operation fehlerhaft war, und das ist der Fehler, den man am häufigsten sieht, wenn Referenz- und Datenanfragen verwechselt werden. Daten anzufordern, während der empfangende Code eine Referenz erwartet, rohe Bytes passwordReference zuzuweisen oder mit einer Zugriffsgruppe abzufragen, die die App nicht nutzen kann, erzeugt ihn allesamt.
Wrapper-Bibliotheken laden dazu ein. Ein Helfer, der Bytes zurückgibt, ist nicht austauschbar mit einem, der eine Referenz zurückgibt, also ist die Reparatur meist eine andere Abfrage statt ein anderes Passwort.
Dieselbe Unstimmigkeit erklärt ein Profil, das anstandslos gespeichert wird und dann keine Verbindung aufbaut: Gespeichert wurde ein Blob, kein Griff, und der Daemon hat nichts aufzulösen.
Speichert sauber, scheitert beim Verbinden
Speichern übt den Kontext Ihrer App. Verbinden übt den des Daemons, und beide haben nicht denselben Zugriff. Ein Profil kann daher jeden Test im Simulator bestehen und beim ersten on-demand-Start scheitern.
Das ist auch die Form des Neustart-Symptoms: Es funktioniert, solange die App offen ist, und stirbt, wenn das System den Tunnel selbst startet.
Prüfen Sie den Hintergrundpfad bewusst vor dem Ausliefern. Lösen Sie eine on-demand-Verbindung bei geschlossener App aus und lesen Sie dann die Gerätekonsole statt das App-Log, denn der Fehler wird von dem Prozess protokolliert, der die Referenz aufzulösen versuchte. Wenn die Konsole zeigt, dass der Daemon den Eintrag ablehnt, ist die Referenz in Ordnung und die Zugänglichkeit des Eintrags das Problem.
Zugänglichkeit entscheidet, ob ein Neustart gelingt
Der Eintrag muss für jeden Systemprozess lesbar sein, der den Tunnel startet, und das ist ein größerer Kreis als Ihre App im Vordergrund. Diese Einschränkung wählt das Zugänglichkeitsattribut für Sie. Ein Eintrag, der bis zum ersten Entsperren geschützt ist, steht einem Start im Hintergrund zur Verfügung, nachdem das Gerät einmal entsperrt wurde; ein Eintrag, der ein jetzt entsperrtes Gerät verlangt, nicht.
Die strengere Klasse zu wählen ist verlockend, und genau sie bricht ein on-demand-Profil auf einem gesperrten Telefon.
Erstellen Sie den Eintrag nach dem Klassenwechsel neu, statt Attribute am vorhandenen zu ändern, und speichern Sie das Protokoll dann mit einer frischen Referenz erneut. Attributänderungen an einem vorhandenen Eintrag erzeugen nicht immer das Verhalten, das die Dokumentation andeutet, und ein neu gebauter Eintrag beseitigt den Zweifel.
Rotation ist der Ort, an dem gespeicherte Profile verrotten
Das Passwort in der Keychain zu ändern ändert nicht automatisch, worauf das gespeicherte Profil zeigt. Den Eintrag zu löschen ist schlimmer: Das Profil behält eine Referenz auf etwas, das nicht mehr existiert, und der Fehler sieht wie eine abgelehnte Authentifizierung aus.
Rotieren Sie in einer Reihenfolge. Aktualisieren Sie den Eintrag, fragen Sie eine neue dauerhafte Referenz ab, speichern Sie das Protokoll erneut und verbinden Sie dann mit geschlossener App, um zu belegen, dass der Daemon sie noch auflösen kann. Halten Sie das Benutzerpasswort und das gemeinsame Geheimnis in getrennten Einträgen, damit jedes nach seinem eigenen Zeitplan rotiert, und protokollieren Sie niemals das Abfrage-Dictionary oder die Referenz selbst. Der Griff ist kein Geheimnis, aber eine Karte dorthin.