# Les IDs de FreeBSD

Une plongée dans le comportement des UID/GID/SVUID de FreeBSD


{{< katex >}}

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.
 */
struct proc {
	TAILQ_HEAD(, thread) p_threads;	/* (c) all threads. */
	struct ucred	*p_ucred;	/* (c) Process owner's identity. */
	struct pwddesc	*p_pd;		/* (b) Cwd, chroot, jail, umask */
	pid_t		p_pid;		/* (b) Process identifier. */
	struct proc	*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.
 */
struct thread {
	lwpid_t		td_tid;		/* (b) Thread ID. */
	struct ucred	*td_realucred;	/* (k) Reference to credentials. */
	struct ucred	*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.
 */
struct ucred {
	struct mtx cr_mtx;
	long	cr_ref;			/* (c) reference count */
	u_int	cr_users;		/* (c) proc + thread using this cred */
	u_int	cr_flags;		/* credential flags */
	struct auditinfo_addr	cr_audit;	/* Audit properties. */
	int	cr_ngroups;		/* number of supplementary groups */
#define	cr_startcopy cr_uid
	uid_t	cr_uid;			/* effective user id */
	uid_t	cr_ruid;		/* real user id */
	uid_t	cr_svuid;		/* saved user id */
	gid_t	cr_gid;			/* effective group id */
	gid_t	cr_rgid;		/* real group id */
	gid_t	cr_svgid;		/* saved group id */
	struct uidinfo	*cr_uidinfo;	/* per euid resource consumption */
	struct uidinfo	*cr_ruidinfo;	/* per ruid resource consumption */
	struct prison	*cr_prison;	/* jail(2) */
	struct loginclass	*cr_loginclass; /* login class */
	void		*cr_pspare2[2];	/* general use 2 */
#define	cr_endcopy	cr_label
	struct label	*cr_label;	/* MAC label */
	gid_t	*cr_groups;		/* groups */
	int	cr_agroups;		/* Available groups */
	/* storage for small groups */
	gid_t   cr_smallgroups[CRED_SMALLGROUPS_NB];
};

```

On a :
- ``cr_uid`` : identifiant utilisateur effectif ((E)UID)
- ``cr_ruid``: identifiant utilisateur réel (RUID)
- ``cr_svuid``: identifiant utilisateur sauvegardé (SVUID)

Et leur équivalent pour les groupes

Maintenant, il faut comprendre comment chacun d'entre eux est utilisé.

## Quand et comment sont-ils mis à jour ?

### EUID

L'identifiant utilisateur effectif, également connu sous le nom d'*UID*, semble être le plus simple

#### Création de processus

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``.

#### En mode ``credential_changing`` (alias SUID)

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.

#### Mode ``!credential_changing``

Les ``euid`` et ``egid`` sont définis à partir des identifiants du processus parent.

Dans les deux cas, ``svuid`` et ``svgid`` seront ensuite copiés à partir de ``euid`` et ``egid``

Au final, on aura toujours :
- ``uid == svuid``
- ``gid == svgid`` 

Vous pouvez lire le code source dans ``sys/kern/kern_exec.c``, dans la fonction ``do_execve``

#### kern_setcred

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.

```c
struct proc *const p = td->td_proc;
// ... 
cred_set = proc_set_cred_enforce_proc_lim(p, new_cred);
```

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.

#### sys_setuid & sys_setreuid & sys_resuid

Ces 3 fonctions mettent toutes à jour l'``euid``, via ``change_euid``.

La différence est simple :
- ``sys_setuid`` : met à jour l'``euid``
- ``sys_setreuid`` : met à jour l'``euid`` et le ``reuid``. Si l'identifiant utilisateur réel (``ruid``) ne correspond pas au nouvel UID, alors le ``svuid`` est mis à jour
- ``sys_setresuid`` : met à jour l'``euid``, le ``reuid`` et le ``svuid``

#### Le rôle de l'(E)UID

D'un point de vue plus large, ``cr_uid`` apparaît \(403\) fois dans FreeBSD à travers \(114\) fichiers. *Ce qui en soi ne signifie pas grand-chose*.

Mais c'est bien plus que ``cr_ruid`` avec \(147\) occurrences, ou ``cr_svuid`` avec \(43\) occurrences.

- Il est utilisé par MAC lors de l'autorisation d'un PRIV(ilège).
- De manière générale, c'est ce que le processus sera aux yeux de l'OS et de tout le monde

### RUID : identifiant utilisateur réel

#### Création de processus

Le RUID est copié depuis le processus créateur.

```c
/*
 * In-kernel implementation of execve().  All arguments are assumed to be
 * userspace pointers from the passed thread.
 */
static int
do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
    struct vmspace *oldvmspace)
{
	struct proc *p = td->td_proc;
	struct ucred *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.


### Identifiant utilisateur sauvegardé

#### Création de processus
Quand un binaire SUID démarre, l'``EUID`` d'origine est sauvegardé ici avant que l'``EUID`` ne soit modifié.

#### Abandon de privilèges

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

## Sources

- [FreeBSD Handbook](https://docs.freebsd.org/en/books/handbook/mac/)
- FreeBSD stable/15 @ ``afaf984ae0dc``
- man 9 ucred

