# FreeBSDs IDs

A dive into FreeBSD's UID/GID/SVUID behaviour


{{< katex >}}

If you have been working with UNIX (Or at least SUS-compliant systems), you most probably already know about UID/GID. 

Of course, it's one of the most easy to grasp concepts there is: You have a User Identifier, and a Group Identifier, assigned to your user

But as most things UNIX, the inner workings are a bit more complicated than it seems

*This article is based on FreeBSD stable/15 @ ``afaf984ae0dc``*


## User ID, Effective User ID and Real User ID

Let's have a look at the kernel's representation of a process: ``sys/sys/proc.h``. Of course, I've elipsed a fair amount of unrelated fields.

Among the hundred of fields in the structure, we can find:

```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. */
};
```

Each process has a list of 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. */
};
```

(This struct is also elipsed for brevity)

What's interesting is the ``ucred`` structure, because it defines the credentials for a thread / proc

```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];
};

```

We have:
- ``cr_uid`` : Effective User ID ((E)UID)
- ``cr_ruid``: Real User ID (RUID)
- ``cr_svuid``: Saved User ID (SVUID)

And their equivalent for groups

Now, we need to understand how each of these is used.

## When and how are they updated ?

### EUID

The effective user ID, also known as the *UID* seems to be the simplest

#### Process creation

When a ``execve`` syscall is triggered, the kernel first checks the permissions of the file and opens the file (``exec_check_permissions``) and then, maps the first page (``exec_map_first_page``)

It creates an ``image_params`` structure that will be the basis for the creation of the process & thread

It then checks for the presence of SUID/SGID bits on the file. If so, it will switch to ``credential_changing`` mode.

#### In ``credential_changing`` mode (aka SUID)

the ``euid`` and ``egid`` of the newly created ``image_param`` will be set to those read from the file (Read during ``exec_check_permissions`` !), depending on the SUID/SGID bits respectively.

#### ``!credential_changing`` mode

The ``euid`` and ``egid`` are set from the parent's process credentials.

In both case, ``svuid`` & ``svgid`` will then be copied from the ``euid`` and ``egid``

In the end, it will always be:
- ``uid == svuid``
- ``gid == svgid`` 

You can read the source in ``sys/kern/kern_exec.c`` in the ``do_execve`` function

#### kern_setcred

There is one way to change the credentials of a process. Through the ``kern_setcred`` function (in ``sys/kern/kern_prot.c``)), we can update a ``proc``'s credentials.

Please note that while the function takes a ``thread*`` as it's first argument, it's indeed the ``proc`` that will be updated.

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

Also, every credential can be changed through this calls.

```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);
```

The changes are however verified in two ways:
- Through MAC for granular access control. Eg. Make sure that the process has the right to update it's values to a given set of new values
- Through ``priv_check_cred``, to make sure that the process has the rights to update it's credentials

*Anyway, MAC will probably be the subject of another post*

Once everything is checked and is in order, then the new privileges are copied in place of the old ones.

#### sys_setuid & sys_setreuid & sys_resuid

These 3 functions all update the ``euid``, via ``change_euid``.

The difference is simple:
- ``sys_setuid`` : Update the ``euid``
- ``sys_setreuid`` : Updates ``euid`` and ``reuid``. If the Real User ID (``ruid``) doesn't match the new UID, then update the ``svuid``
- ``sys_setresuid`` : Updates ``euid``, ``reuid`` and ``svuid``

#### The role of (E)UID

From a farther standpoint, ``cr_uid`` appears \(403\) times in FreeBSD across \(114\) files. *Which in itself doesn't mean much*.

But, it's much more than ``cr_ruid`` with \(147\) matches, or ``cr_svuid`` with \(43\) matches.

- It's used by MAC when allowing a PRIV(ilege).
- Generally, it's who the process will be to the eye of the OS and everyone else

### RUID: Real User ID

#### Process creation

The RUID is copied from the creating process.

```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); 
            // ...
		}
    }

```


Whether RUID == EUID depends on the SUID/SGID bits.


### Saved User ID

#### Process creation
When a SUID binary starts, the origin ``EUID`` is saved here before the ``EUID`` is changed.

#### Privilege dropping

The svuid is saved on process creation.

When you call ``setuid`` to change the UID of a process, the ``EUID`` is updated and the ``cr_svuid`` remains unchanged. When you call again ``setuid(uid_t uid)``, if  ``uid == cr_svuid``, you are allowed to return to your original ``uid``

## Sources

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

