Une plongée dans le comportement des UID/GID/SVUID de FreeBSD
Si vous avez déjà travaillé avec UNIX (ou du moins des systèmes conformes à la SUS), vous connaissez très probablement déjà les UID/GID.
Bien sûr, c’est l’un des concepts les plus simples à saisir qui soit : vous avez un identifiant utilisateur, et un identifiant de groupe, assignés à votre utilisateur
Mais comme la plupart des choses sous UNIX, le fonctionnement interne est un peu plus compliqué qu’il n’y paraît
Cet article se base sur FreeBSD stable/15 @ afaf984ae0dc
ID utilisateur, ID utilisateur effectif et ID utilisateur réel
#
Jetons un œil à la représentation d’un processus par le noyau : sys/sys/proc.h. Bien sûr, j’ai éludé une bonne partie des champs non pertinents.
Parmi la centaine de champs de la structure, on trouve :
c
/*
* Process structure.
*/structproc{TAILQ_HEAD(,thread)p_threads;/* (c) all threads. */structucred*p_ucred;/* (c) Process owner's identity. */structpwddesc*p_pd;/* (b) Cwd, chroot, jail, umask */pid_tp_pid;/* (b) Process identifier. */structproc*p_pptr;/* (c + e) Pointer to parent process. */LIST_HEAD(,proc)p_children;/* (e) Pointer to list of children. */};
Chaque processus possède une liste de threads :
c
/*
* Kernel runnable context (thread).
* This is what is put to sleep and reactivated.
* Thread context. Processes may have multiple threads.
*/structthread{lwpid_ttd_tid;/* (b) Thread ID. */structucred*td_realucred;/* (k) Reference to credentials. */structucred*td_ucred;/* (k) Used credentials, temporarily switchable. */};
(Cette structure est également éludée pour plus de concision)
Ce qui est intéressant, c’est la structure ucred, car elle définit les identifiants d’un thread / processus
c
/*
* Credentials.
*
* Please do not inspect cr_uid directly to determine superuserness. The
* priv(9) interface should be used to check for privilege.
*
* Lock reference:
* c - cr_mtx
*
* Unmarked fields are constant after creation.
*
* See "Credential management" comment in kern_prot.c for more information.
*/structucred{structmtxcr_mtx;longcr_ref;/* (c) reference count */u_intcr_users;/* (c) proc + thread using this cred */u_intcr_flags;/* credential flags */structauditinfo_addrcr_audit;/* Audit properties. */intcr_ngroups;/* number of supplementary groups */#define cr_startcopy cr_uid
uid_tcr_uid;/* effective user id */uid_tcr_ruid;/* real user id */uid_tcr_svuid;/* saved user id */gid_tcr_gid;/* effective group id */gid_tcr_rgid;/* real group id */gid_tcr_svgid;/* saved group id */structuidinfo*cr_uidinfo;/* per euid resource consumption */structuidinfo*cr_ruidinfo;/* per ruid resource consumption */structprison*cr_prison;/* jail(2) */structloginclass*cr_loginclass;/* login class */void*cr_pspare2[2];/* general use 2 */#define cr_endcopy cr_label
structlabel*cr_label;/* MAC label */gid_t*cr_groups;/* groups */intcr_agroups;/* Available groups *//* storage for small groups */gid_tcr_smallgroups[CRED_SMALLGROUPS_NB];};
Lorsqu’un appel système execve est déclenché, le noyau vérifie d’abord les permissions du fichier et ouvre le fichier (exec_check_permissions) puis mappe la première page (exec_map_first_page)
Il crée une structure image_params qui servira de base à la création du processus et du thread
Il vérifie ensuite la présence des bits SUID/SGID sur le fichier. Si c’est le cas, il passe en mode credential_changing.
les euid et egid du image_param nouvellement créé seront définis à partir de ceux lus dans le fichier (lus pendant exec_check_permissions !), selon les bits SUID/SGID respectivement.
Il existe un moyen de modifier les identifiants d’un processus. Via la fonction kern_setcred (dans sys/kern/kern_prot.c)), on peut mettre à jour les identifiants d’un proc.
Veuillez noter que bien que la fonction prenne un thread* comme premier argument, c’est bien le proc qui sera mis à jour.
De plus, chaque identifiant peut être modifié via ces appels.
c
/*
* Change user IDs.
*/if(flags&SETCREDF_UID)change_euid(new_cred,uip);if(flags&SETCREDF_RUID)change_ruid(new_cred,ruip);if(flags&SETCREDF_SVUID)change_svuid(new_cred,wcred->sc_svuid);/*
* Change groups.
*/if(flags&SETCREDF_SUPP_GROUPS)crsetgroups_internal(new_cred,wcred->sc_supp_groups_nb,wcred->sc_supp_groups);if(flags&SETCREDF_GID)change_egid(new_cred,wcred->sc_gid);if(flags&SETCREDF_RGID)change_rgid(new_cred,wcred->sc_rgid);if(flags&SETCREDF_SVGID)change_svgid(new_cred,wcred->sc_svgid);
Les modifications sont cependant vérifiées de deux façons :
Via MAC pour un contrôle d’accès granulaire. Par exemple, s’assurer que le processus a le droit de mettre à jour ses valeurs vers un nouvel ensemble de valeurs données
Via priv_check_cred, pour s’assurer que le processus a le droit de mettre à jour ses identifiants
Quoi qu’il en soit, MAC fera probablement l’objet d’un autre article
Une fois que tout est vérifié et en ordre, les nouveaux privilèges sont copiés à la place des anciens.
/*
* In-kernel implementation of execve(). All arguments are assumed to be
* userspace pointers from the passed thread.
*/staticintdo_execve(structthread*td,structimage_args*args,structmac*mac_p,structvmspace*oldvmspace){structproc*p=td->td_proc;structucred*oldcred;// ...
oldcred=p->p_ucred;// ...
if(credential_changing&&#ifdef CAPABILITY_MODE
((oldcred->cr_flags&CRED_FLAG_CAPMODE)==0)&&#endif
(imgp->vp->v_mount->mnt_flag&MNT_NOSUID)==0&&(p->p_flag&P_TRACED)==0){imgp->newcred=crdup(oldcred);// ...
}else{if(oldcred->cr_svuid!=oldcred->cr_uid||oldcred->cr_svgid!=oldcred->cr_gid){// ...
imgp->newcred=crdup(oldcred);// ...
}}
Le fait que RUID == EUID dépend des bits SUID/SGID.
Le svuid est sauvegardé à la création du processus.
Quand vous appelez setuid pour changer l’UID d’un processus, l’EUID est mis à jour et le cr_svuid reste inchangé. Quand vous appelez à nouveau setuid(uid_t uid), si uid == cr_svuid, vous êtes autorisé à revenir à votre uid d’origine
Un guide pas à pas testé pour activer le Profile Guided Optimization et le Link Time Optimization dans un projet Meson compilé avec Clang, y compris l'étape de fusion llvm-profdata que la plupart des guides oublient.
Avant de toucher la moindre ligne de ma simulation de fluide, j'ai laissé le compilateur faire le travail. Changer simplement les flags m'a fait passer de 1x à environ 40x.