Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-068 — Surface fine credentials (uid/gid) en couche 1 (air-process), bindée par la libc

Statut : Accepté (2026-07-05, ratification BDFL — y compris la sémantique POSIX setuid/setgid du § dédié). (ADR-015). Sous la délégation d’ADR-065 (additifs révélés par les toits).

Catégorie : Descellement additif de la couche 1 scellée (couche-1-v1.3couche-1-v1.4). Aucune rupture de signature existante.

Contexte

La séparation de privilèges d’OpenSSH exige la famille unistd/grp des identifiants : getuid/geteuid/getgid/getegid (contrôles), setuid/setgid/ setresuid/setresgid (largage de privilèges), getgroups/setgroups, initgroups. sshd démarre root, résout l’utilisateur (getpwnam, fait) puis largue dans un ordre strict.

Ce que la couche 0 a DÉJÀ (aucun additif couche 0)

air-sys-syscall::process expose, en variantes modernes typées (ADR-021) : get_resuid/set_resuid (triplet ResUid), get_resgid/set_resgid (ResGid), get_groups/set_groups. Rien à ajouter en couche 0.

Le manque : une surface FINE en couche 1

air-process (couche 1) n’expose que drop_privileges (la séquence orchestrée setgroups→setresgid→setresuid + vérification + seccomp/Landlock). Les briques à-la-carte (lire l’identité courante, poser un seul triplet uid/gid, poser les groupes) — les 1:1 dont la libc a besoin — ne sont pas exposées. C’est un manque couche 1, pas couche 0.

Décision

Module air-process::credentials (couche 1)

Surface mince et typée sur la couche 0 (zéro réimplémentation) :

pub struct AirProcessCredentials { pub uid: ResUid, pub gid: ResGid }
impl AirProcessCredentials { pub fn current() -> AirResult<Self>; }  // get_resuid + get_resgid

pub fn set_uids(new: ResUid) -> AirResult<()>;             // = set_resuid
pub fn set_gids(new: ResGid) -> AirResult<()>;             // = set_resgid
pub fn supplementary_groups(buf: &mut [Gid]) -> AirResult<&[Gid]>;  // = get_groups
pub fn set_supplementary_groups(groups: &[Gid]) -> AirResult<()>;   // = set_groups

drop_privileges reste la voie recommandée pour le largage réel (elle vérifie + pose seccomp/Landlock) ; ces briques à-la-carte sont plus bas niveau, pour la conformité libc.

Binders libc (couche 2)

  • Getters (infaillibles) : getuidcurrent().uid.real, geteuid.effective, getgid/getegid (idem gid).
  • setres*/groupes (explicites, non ambigus) : setresuid(r,e,s)set_uids, setresgidset_gids, getgroups/setgroups→les fns de groupes.
  • initgroups(user, gid) = air_account::groups_of_user(user) + gidset_supplementary_groups.

Sémantique POSIX setuid/setgid — REMONTÉE AU BDFL

setuid(uid) a une sémantique conditionnelle au privilège (source de bugs sécurité historiques) :

  • Privilégié (euid == 0) : pose real + effective + saved = uid (largage irréversible — ce que veut sshd).
  • Non privilégié : pose uniquement l’effectif, et seulement si uid ∈ {real, saved} ; sinon EPERM.

Proposition : implémenter exactement cette sémantique au-dessus de set_uids (idem setgid/setresgid). Comme c’est le point sécurité-sensible de cet ADR, il est remonté : à ratifier avant implémentation de setuid/setgid (les getters + setres* + groupes, eux, sont non ambigus et peuvent précéder).

Gouvernance

  • Module air-process::credentials = additif → re-sceau couche-1-v1.4 (signé) au merge. drop_privileges inchangé.
  • Sous la délégation ADR-065 ; volet sécurité (setuid/setgid privilégié) remonté explicitement au BDFL.

Conséquences

  • air-process gagne credentials (getters + set à-la-carte).
  • Binders libc : getuid/geteuid/getgid/getegid/setresuid/setresgid/ getgroups/setgroups (+ setuid/setgid après ratification) + initgroups.
  • Tag couche-1-v1.4 ; registre docs/libc-conformance.md mis à jour.

Alternatives rejetées

  • Faire la libc appeler drop_privileges. Rejeté : drop_privileges est une opération composée (tout ou rien, + seccomp/Landlock) ; la libc a besoin des primitives 1:1 POSIX, pas de l’orchestration.
  • Réimplémenter les syscalls uid/gid en couche 1/2. Sans objet : la couche 0 les a déjà (set_resuid/…), la couche 1 ne fait que les surfacer finement.
  • Additif couche 0. Sans objet (déjà présent).

Suite

  • Après ratification : air-process::credentials, binders libc, initgroups, air-process::drop_to_user (résout via air-account + drop_privileges). Tag couche-1-v1.4. Note de re-sceau dans INDEX.md + etat-avancement.md.