Process in FreeBSD

プロセスに関する構造体、struct procsys/proc.hに定義されている。Linuxでいうtask_structみたいな感じ。

procの方がシンプルで、task_structは定義だけ800行以上あるが、procは128行しか無い。

src

コメントがわかりやすくて助かる。

このproc.hで面白いのが

extern struct proclist allproc;		/* List of all processes. */

で、名前とコメントの通りすべてのプロセスがリストになっている。

リストについては過去の記事を参照

LIST_HEAD(proclist, proc);

proclistはこうなっている。

see_other_uids

FreeBSDのセキュリティ機構として、see_other_(u|g)idsがある。

他のユーザのプロセスを参照できなくするというもの

demo

まずは無効になっている状態

[root@freebsd13 ~]# sysctl security.bsd.see_other_uids=1
security.bsd.see_other_uids: 1 -> 1
[root@freebsd13 ~]# sysctl security.bsd.see_other_gids=1
security.bsd.see_other_gids: 1 -> 1
[root@freebsd13 ~]# cat &
[1] 983

見えてる

[vagrant@freebsd13 ~]$ ps aux | grep cat | grep -v grep
root    983   0.0  0.1  12720 2296  2  T    00:45    0:00.00 cat

有効化する

[root@freebsd13 ~]# sysctl security.bsd.see_other_uids=0
security.bsd.see_other_uids: 1 -> 0

[1]+  Stopped                 cat
[root@freebsd13 ~]# sysctl security.bsd.see_other_gids=0
security.bsd.see_other_gids: 1 -> 0
[root@freebsd13 ~]#

見えなくなる

[vagrant@freebsd13 ~]$ ps aux | grep cat | grep -v grep
[vagrant@freebsd13 ~]$

実装

sys/kern/kern_prot.cを見ると、

/*
 * 'see_other_uids' determines whether or not visibility of processes
 * and sockets with credentials holding different real uids is possible
 * using a variety of system MIBs.
 * XXX: data declarations should be together near the beginning of the file.
 */
static int	see_other_uids = 1;
SYSCTL_INT(_security_bsd, OID_AUTO, see_other_uids, CTLFLAG_RW,
    &see_other_uids, 0,
    "Unprivileged processes may see subjects/objects with different real uid");

/*-
 * Determine if u1 "can see" the subject specified by u2, according to the
 * 'see_other_uids' policy.
 * Returns: 0 for permitted, ESRCH otherwise
 * Locks: none
 * References: *u1 and *u2 must not change during the call
 *             u1 may equal u2, in which case only one reference is required
 */
int
cr_canseeotheruids(struct ucred *u1, struct ucred *u2)
{

    if (!see_other_uids && u1->cr_ruid != u2->cr_ruid) {
        if (priv_check_cred(u1, PRIV_SEEOTHERUIDS) != 0)
            return (ESRCH);
    }
    return (0);
}

こんな感じで定義されている。

cr_canseeotheruidsは、同じファイルのcr_canseeで呼ばれており、

/*-
 * Determine if u1 "can see" the subject specified by u2.
 * Returns: 0 for permitted, an errno value otherwise
 * Locks: none
 * References: *u1 and *u2 must not change during the call
 *             u1 may equal u2, in which case only one reference is required
 */
int
cr_cansee(struct ucred *u1, struct ucred *u2)
{
    int error;

    if ((error = prison_check(u1, u2)))
        return (error);
#ifdef MAC
    if ((error = mac_cred_check_visible(u1, u2)))
        return (error);
#endif
    if ((error = cr_canseeotheruids(u1, u2)))
        return (error);
    if ((error = cr_canseeothergids(u1, u2)))
        return (error);
    if ((error = cr_canseejailproc(u1, u2)))
        return (error);
    return (0);
}

こんな感じで、他の検証も含めて、そのユーザが見れるかどうかをチェックしている。

プロセスについては

/*-
 * Determine if td "can see" the subject specified by p.
 * Returns: 0 for permitted, an errno value otherwise
 * Locks: Sufficient locks to protect p->p_ucred must be held.  td really
 *        should be curthread.
 * References: td and p must be valid for the lifetime of the call
 */
int
p_cansee(struct thread *td, struct proc *p)
{
    /* Wrap cr_cansee() for all functionality. */
    KASSERT(td == curthread, ("%s: td not curthread", __func__));
    PROC_LOCK_ASSERT(p, MA_OWNED);

    if (td->td_proc == p)
        return (0);
    return (cr_cansee(td->td_ucred, p->p_ucred));
}

このp_canseeで検証が行われる。

実際に確認しているところを見る

sys/fs/procfs/procfs_status.cでは

int
procfs_doproccmdline(PFS_FILL_ARGS)
{

    /*
     * If we are using the ps/cmdline caching, use that.  Otherwise
     * read argv from the process space.
     * Note that if the argv is no longer available, we deliberately
     * don't fall back on p->p_comm or return an error: the authentic
     * Linux behaviour is to return zero-length in this case.
     */

    PROC_LOCK(p);
    if (p->p_args && p_cansee(td, p) == 0) {
        sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
        PROC_UNLOCK(p);
        return (0);
    }

という感じで、p_canseeの値を参照している。

BSD、読みやすい。

おわりに

この記事はn01e0 Advent Calendar 2023の19日目の記事です。

また、IPFactory OB Advent Calendar 2023の19日目の記事も兼ねています。

明日はあるかわかりません