Profilo VPN iOS: passwordReference, non la password
Un riferimento del portachiavi è una maniglia, non il segreto. Passare byte spiega l’errore -50, il guasto al riavvio e la trappola della rotazione nei profili VPN salvati.
Installi un profilo, il tunnel si alza e tutto sembra finito. Poi il telefono si riavvia, on-demand risveglia la connessione senza app in primo piano e il tunnel rifiuta di partire. Il profilo è ancora lì e la password è ancora corretta, ed è per questo che questo guasto manda a cercare nel posto sbagliato. Una Network Extension non può leggere il tuo segreto dal profilo: le serve un riferimento del portachiavi che possa risolvere da sola.
Il percorso qui sotto segue un solo tentativo di connessione attraverso quel passaggio, dalla query che costruisce il riferimento al riavvio che lo mette alla prova.
Il profilo memorizza una maniglia, non il segreto
NEVPNProtocol espone passwordReference e sharedSecretReference per un motivo. Quando il demone avvia un tunnel può non girare dentro il tuo processo, quindi non può chiamare il tuo codice e chiedere una password. Legge un riferimento persistente del portachiavi, lo risolve nel proprio contesto e recupera l’elemento da sé.
Costruisci quel riferimento chiedendolo al portachiavi, che è una richiesta diversa da quella dei dati:
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
]Consegna l’oggetto restituito al protocollo, non i dati dell’elemento. In pratica il profilo registra dove vive il segreto, ed è anche per questo che un backup della configurazione non duplica silenziosamente la password in un plist.
Perché l’API risponde -50
errSecParam significa che la richiesta o il valore erano malformati per l’operazione, ed è l’errore che si vede più spesso quando si confondono le richieste di riferimento e di dati. Chiedere dati mentre il codice ricevente aspetta un riferimento, assegnare byte grezzi a passwordReference o interrogare con un gruppo di accesso che l’app non può usare, tutte lo producono.
Le librerie wrapper invitano a questo. Un helper che restituisce byte non è intercambiabile con uno che restituisce un riferimento, quindi la riparazione è di solito un’altra query e non un’altra password.
La stessa discordanza spiega un profilo che si salva senza lamentarsi e poi non si connette: ciò che è stato memorizzato è un blob, non una maniglia, e il demone non ha nulla da risolvere.
Si salva bene, fallisce alla connessione
Il salvataggio esercita il contesto della tua app. La connessione esercita quello del demone, e i due non hanno lo stesso accesso. Un profilo può quindi superare ogni test nel simulatore e fallire al primo avvio on-demand.
È anche la forma del sintomo al riavvio: funziona finché l’app è aperta e muore quando il sistema avvia il tunnel da solo.
Prova deliberatamente il percorso in background prima di distribuire. Attiva una connessione on-demand con l’app chiusa, poi leggi la console del dispositivo invece del log dell’app, perché il guasto viene registrato dal processo che ha tentato di risolvere il riferimento. Se la console mostra che il demone rifiuta l’elemento, il riferimento è a posto e il problema è l’accessibilità dell’elemento.
L’accessibilità decide se un riavvio funziona
L’elemento deve essere leggibile dal processo di sistema che avvia il tunnel, e questo è un pubblico più ampio della tua app in primo piano. Quel vincolo sceglie l’attributo di accessibilità per te. Un elemento protetto fino al primo sblocco è disponibile per un avvio in background dopo che il dispositivo è stato sbloccato una volta; un elemento che richiede che il dispositivo sia sbloccato adesso non lo è.
Scegliere la classe più severa è tentante ed è esattamente ciò che rompe un profilo on-demand su un telefono bloccato.
Dopo aver cambiato classe, ricrea l’elemento invece di modificare gli attributi su quello esistente, poi salva di nuovo il protocollo con un riferimento nuovo. Le modifiche agli attributi su un elemento esistente non producono sempre il comportamento che la documentazione lascia intendere, e un elemento ricostruito elimina il dubbio.
La rotazione è dove i profili salvati marciscono
Cambiare la password nel portachiavi non cambia automaticamente ciò a cui punta il profilo salvato. Cancellare l’elemento è peggio: il profilo conserva un riferimento a qualcosa che non esiste più, e il guasto sembra un rifiuto di autenticazione.
Ruota in un solo ordine. Aggiorna l’elemento, interroga un nuovo riferimento persistente, salva di nuovo il protocollo, poi connettiti con l’app chiusa per dimostrare che il demone può ancora risolverlo. Tieni la password utente e il segreto condiviso in elementi separati perché ciascuno ruoti secondo il proprio calendario, e non registrare mai il dizionario della query né il riferimento stesso. La maniglia non è un segreto, ma è una mappa verso uno.