# L'identité d'un processus macOS dépasse le Team ID

Un Team ID ne suffit pas pour décider quel processus macOS peut utiliser un secret. Il identifie une équipe de développement Apple, pas un exécutable précis. L'autoriser seul donne potentiellement le même accès à toutes les applications, tous les outils et tous les futurs binaires correctement signés par cette équipe.

Une identité défendable combine l'autorité de signature et l'identifiant de signature, conserve une exigence compatible avec les mises à jour légitimes et l'évalue sur le processus qui formule réellement la demande. Le chemin reste utile dans l'écran d'approbation et l'audit, mais ne doit pas décider. Les builds non signés ou signés ad hoc exigent un circuit distinct, clairement plus faible.

Une signature indique qui a signé et quel nom de programme ce signataire revendique. Elle ne prouve ni que le processus mérite un mot de passe, ni que sa demande est raisonnable, ni qu'un autre programme du même développeur doit recevoir le même droit. L'identité alimente l'autorisation, elle ne la remplace pas.

## Le Team ID identifie le signataire, pas le programme

Le Team ID indique quelle équipe Apple détient l'identité de signature. Pour un certificat développeur émis par Apple, le langage d'exigences l'expose dans l'unité organisationnelle du certificat feuille. Les outils peuvent aussi afficher `TeamIdentifier`.

Inspectez l'exécutable exact, pas seulement le bundle externe :

```sh
codesign -dvvv /Applications/Example.app/Contents/MacOS/Example 2>&1
```

La sortie utile ressemble à ceci :

```text
Executable=/Applications/Example.app/Contents/MacOS/Example
Identifier=dev.example.agent
Authority=Developer ID Application: Example Developer (A1B2C3D4E5)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
TeamIdentifier=A1B2C3D4E5
```

Un Team ID valide délimite le signataire et exclut les autres équipes. La documentation Apple SigningIdentifier précise que plusieurs signataires peuvent revendiquer le même identifiant. Pour du code tiers, une vérification sûre exige donc aussi TeamIdentifier et une catégorie de validation adaptée.

Mais une équipe peut signer une application, son auxiliaire privilégié, un élément d'ouverture de session, un outil en ligne de commande, un service XPC et un diagnostic interne. Tous partagent éventuellement le même Team ID. Un service de signature compromis peut encore produire un nouveau binaire dans ce périmètre. L'équipe ne distingue aucun de ces programmes.

`Team ID == équipe approuvée` accorde donc beaucoup trop pour un secret. Cela revient à ouvrir une porte à toute une entreprise parce que les badges ont le même émetteur. L'émetteur compte, tout comme le nom inscrit sur chaque badge.

Une politique peut volontairement faire confiance à toute l'équipe pour une ressource locale jetable. Il faut alors la nommer « confiance d'équipe », et non identité d'application, afin que son étendue reste visible.

## L'identifiant de signature sépare les programmes

L'identifiant de signature apporte le nom du programme dans l'espace du signataire. `codesign` l'affiche sous `Identifier`. Il correspond souvent à l'identifiant du bundle, sans que macOS l'impose, et un exécutable en ligne de commande peut en porter un sans bundle.

La paire minimale devient :

```text
team_id = A1B2C3D4E5
signing_identifier = dev.example.agent
```

Elle signifie « le programme `dev.example.agent` signé par l'équipe `A1B2C3D4E5` ». Une autre équipe peut copier le nom mais pas satisfaire la contrainte d'équipe; un autre programme de l'équipe devrait employer un autre identifiant.

Le signataire contrôle toutefois son espace de noms et peut réutiliser un identifiant. Une configuration défectueuse peut donner celui de l'application à un auxiliaire, et un signataire compromis peut copier la valeur approuvée. La paire suppose donc que le signataire protège ses certificats et ses noms.

Ce compromis reste adapté aux mises à jour. Un hash est plus précis mais change à chaque version; l'empreinte d'un certificat casse lors de son renouvellement. Team ID et identifiant acceptent les futures versions du même programme sans accepter tout le catalogue.

Inspectez chaque exécutable qui se connecte. TN3127 montre qu'une application et son outil intégré partagent l'équipe mais doivent avoir des identifiants différents. Si l'outil appelle le service, son identité compte.

Comparez l'identifiant octet par octet. Apple indique qu'aucune normalisation Unicode n'a lieu. Ne le mettez pas en minuscules et ne le reconstruisez pas depuis `CFBundleIdentifier`.

## L'exigence désignée conserve l'identité entre les versions

L'exigence désignée, ou DR, est l'expression macOS qui décide si un code est le même qu'auparavant. Elle combine le nom du programme et les contraintes sur le signataire.

TN3127 l'illustre par une mise à jour : la version 1.3 doit satisfaire l'identité enregistrée pour 1.2 malgré des octets différents, tandis qu'un autre produit doit échouer. C'est le comportement attendu d'une autorisation durable.

Affichez la DR ainsi :

```sh
codesign -d -r- /Applications/Example.app/Contents/MacOS/Example 2>&1
```

Une signature Developer ID produit généralement cette forme :

```text
Executable=/Applications/Example.app/Contents/MacOS/Example
designated => anchor apple generic and identifier "dev.example.agent" and certificate leaf[subject.OU] = "A1B2C3D4E5"
```

Ne parsez pas ce texte pour réécrire l'évaluateur Apple. TN3125 rappelle que les structures changent et recommande `codesign` ou Code Signing Services. Obtenez l'objet avec `SecCodeCopyDesignatedRequirement`, puis évaluez-le avec `SecCodeCheckValidity` ou l'API de processus actuelle.

La DR du code reste une revendication, pas une permission. Le signataire peut en fournir une et le système peut en synthétiser une. Votre service décide laquelle enregistrer et si son périmètre convient.

Pour un logiciel Developer ID ordinaire, enregistrez la DR validée et conservez Team ID et identifiant comme faits lisibles. Évaluez ensuite le processus vivant contre l'objet, sans comparer deux rendus textuels.

Un changement entre Mac App Store et Developer ID peut modifier la DR. TN3127 explique que les valeurs par défaut ne sont pas automatiquement compatibles. Traitez ce passage comme une migration d'identité et demandez une nouvelle approbation, sauf exigence de compatibilité explicitement conçue et testée.

## Le chemin fournit du contexte, pas une preuve

Le chemin montre où macOS a trouvé l'image. Il aide l'opérateur, mais les fichiers se déplacent, la translocation change les emplacements et les gestionnaires installent des versions ailleurs. À l'inverse, remplacer un fichier dans un chemin autorisé hérite d'une permission fondée uniquement sur ce chemin.

Une liste de chemins rejette donc le même programme après un déplacement et accepte un autre fichier après remplacement. Propriétaire et permissions réduisent certains risques sans créer une identité cryptographique.

Gardez le chemin pour :

- Montrer quelle installation a demandé l'accès.
- Conserver du contexte d'enquête.
- Ajouter une contrainte d'emplacement après la validation de signature.

Dans un environnement géré, vous pouvez exiger à la fois une DR et un répertoire appartenant à root. Le chemin restreint alors un programme déjà identifié; il ne répare jamais une signature absente.

Observez le chemin lié au processus vivant. Ne croyez pas `argv[0]`, le répertoire courant, une valeur de requête ou une variable d'environnement. Même un chemin canonique peut subir une course entre inspection et utilisation.

Conservez éventuellement chemin observé et chemin résolu dans l'audit, mais pas dans l'identité durable. Placez toute règle dans `location_constraint` pour la distinguer de l'identité.

## Évaluez l'appelant vivant plutôt qu'un chemin

La décision doit se lier au processus connecté. Inspecter un fichier lors de l'installation laisse une fenêtre de remplacement, et relire le chemin au moment de la demande peut examiner un autre fichier que l'image déjà exécutée.

Code Signing Services distingue le code statique du code dynamique. Apple documente `SecCodeCopyGuestWithAttributes` pour obtenir un objet de processus et `SecCodeCheckValidityWithProcessRequirement` pour l'évaluer. Employez l'API prise en charge sur votre cible, sans analyser `codesign` en production.

Le flux fiable est le suivant :

1. Tirez l'identité du canal IPC fourni par le noyau, par exemple son jeton d'audit, jamais d'un PID dans le corps.
2. Résolvez le processus vivant et validez sa signature.
3. Évaluez l'exigence enregistrée sur ce même objet.
4. Journalisez chemin, PID, identité de démarrage disponible, faits de signature et résultat.
5. Liez l'approbation à la connexion ou au processus et supprimez-la à sa fin.

Un PID seul est réutilisable. Un jeton d'audit contient davantage d'identité et la vérification liée à la connexion réduit la course. XPC et les sockets Unix ont des API différentes, mais le sujet doit toujours venir de la vision du noyau.

Validez avant d'afficher. Une carte doit distinguer une « signature Developer ID valide » d'un simple texte d'identifiant. En cas d'échec, ne revenez pas à une correspondance de chemin.

Si un agent signé lance `/bin/sh` et que le shell se connecte, le shell est le pair. Remonter vers le parent peut prêter son autorité à un enfant substitué. Pour autoriser le lanceur, transmettez une capacité limitée sur un canal contrôlé.

L'approbation de session doit expirer à la sortie du processus. Une identité reconnue peut simplifier une future approbation, mais ne doit pas créer une permission permanente pour tout processus correspondant.

## Les builds non signés ont besoin d'une autre politique

Le code non signé n'a pas de DR. Une signature ad hoc produit une DR liée à cette version et une recompilation la change. TN3127 indique que macOS ne peut suivre durablement ces deux catégories.

Pour des secrets de production, refusez un appelant sans signature valide et stable. Les développeurs peuvent signer localement avec Apple Development ou une identité privée gérée par l'organisation.

Si le développement exige un mode faible, rendez-le volontaire et visible. Liez `approbation de développement` à la vie du processus et au hash exact, affichez `non signé` ou `ad hoc`, excluez les secrets de production et redemandez après chaque build.

N'utilisez pas :

- Un chemin du dossier personnel, remplaçable par l'utilisateur.
- Un nom lu dans des métadonnées non signées.
- La signature du parent quand l'enfant se connecte.
- Une autorisation générale du terminal.
- Un hash comme identité permanente malgré les recompilations.

Le hash peut limiter une exception temporaire : il signifie « ces octets pour cette exécution », pas « le même programme après mise à jour ».

Traitez un Team ID absent comme une catégorie, pas comme une valeur qui contourne la comparaison. Code Apple, signature indépendante, ad hoc et absence de signature exigent des tests distincts.

## Enregistrez une identité dont la portée reste explicite

Le dossier durable doit contenir exigence évaluée, faits lisibles, catégorie, contrainte d'emplacement et portée. Il doit dire si l'approbation vise une exécution, de futures versions ou toute l'équipe.

Exemple, avec un blob d'exigence fourni par Code Signing Services :

```json
{
  "schema": 1,
  "code_category": "developer_id",
  "team_id": "A1B2C3D4E5",
  "signing_identifier": "dev.example.agent",
  "designated_requirement": "BASE64_PLATFORM_REQUIREMENT",
  "approval_scope": "matching_identity_per_session",
  "location_constraint": null,
  "observed_path": "/Applications/Example.app/Contents/MacOS/Example"
}
```

L'algorithme classe et valide l'appelant, évalue l'exigence, vérifie la cohérence des faits, applique l'emplacement puis la portée et les approbations du secret. Une incohérence signale une corruption ou un bogue, jamais un motif d'élargissement.

Son échec doit être banal et explicite :

```text
caller = peer_from_kernel(connection)
code = dynamic_code(caller)
result = validate(code)

if result.category not in accepted_categories:
    deny("unsupported code category")

identity = evaluate(stored_requirement, code)
if identity != satisfied:
    deny("process identity changed")

if facts(code) != stored_audit_facts:
    deny("identity record inconsistent")

if location_constraint and not location_allowed(code, location_constraint):
    deny("approved program ran from an unapproved location")

authorize_for(connection_lifetime, requested_secret)
```

Il n'accepte ni Team ID seul, ni chemin avant signature, ni ancêtre plus commode, ni conversion silencieuse d'un appelant non signé. Chaque refus explique sa cause.

Traitez transfert d'équipe, nouvel identifiant, nouveau canal et passage d'ad hoc à Developer ID comme des migrations approuvées. Gardez ancien et nouveau dossiers dans l'audit.

Testez deux identifiants sous la même équipe, le même identifiant sous une autre équipe, un déplacement, un remplacement après lancement et une recompilation ad hoc. Seule l'identité voulue doit passer.

## L'identité ne décide pas si l'action est permise

Une correspondance correcte identifie le sujet, sans lui donner tous les secrets, toutes les destinations ni une durée illimitée. Séparez identité, approbation de session et approbation d'action.

Sinon, une permission pour un jeton de développement peut devenir plus tard un accès à toutes les informations d'authentification. Ni Team ID, ni identifiant, ni DR ne portent la limite de ressource initiale.

Écrivez sujet, action, ressource, conditions et durée :

```text
subject: requirement R42 satisfied by this live process
action: perform an HTTP request with injected bearer credentials
resource: issue-tracker-development
conditions: vault unlocked and session approved
lifetime: this connection, with per-call approval if the secret requires it
```

Le sujet référence le processus; les autres champs viennent de l'opération et de ses contrôles. Un agent peut ainsi utiliser HTTP et SSH sans que sa reconnaissance autorise automatiquement les deux.

Un entitlement signé n'est pertinent que si la politique lui donne un sens précis. Sa présence ne rend pas un programme sûr, et son absence n'affaiblit pas la paire Team ID et identifiant.

Notarisation et Gatekeeper répondent à d'autres questions. `spctl -a -vv -t exec` aide à diagnostiquer l'acceptation d'un fichier, sans remplacer l'exigence enregistrée.

Révoquer une session doit fermer la connexion; révoquer une identité doit imposer une future approbation; révoquer une ressource doit préserver les autres. Un booléen `trusted` ne peut exprimer ces choix.

L'audit peut alors établir quel programme signé a tourné, qui a approuvé cette exécution et quelle opération elle a reçue. Une DR parfaite ne répond pas seule à la question de l'autorité accordée.

## L'écran d'approbation doit dire ce qui est prouvé

Affichez signataire ou équipe, identifiant, catégorie, chemin et durée, car un humain ne relit pas un blob. Un nom convivial, issu de métadonnées modifiables, ne doit rester qu'une étiquette secondaire.

La première demande d'un nouveau processus agent dans Sallyport affiche une carte menée par son autorité de signature, puis lie l'approbation à l'exécution jusqu'à sa fin. Le verrou du coffre et l'approbation par appel restent des contrôles séparés.

L'audit conserve catégorie, Team ID, identifiant, référence d'exigence, chemin, processus, session, décision, motif et portée. Une enquête future ne doit pas dépendre du fichier présent aujourd'hui dans l'ancien chemin.

N'appelez pas le résultat `processus fiable`. La preuve est plus étroite : ce processus vivant a satisfait une exigence et une personne ou une politique lui a accordé une action définie.

Le Team ID nomme le signataire, l'identifiant nomme son programme, la DR préserve la continuité et l'objet dynamique lie la preuve à l'appelant. Si une pièce manque, réduisez l'accès ou redemandez. L'incertitude ne doit jamais devenir une permission permanente.
