FreeBSD 系统结构手册
上一页 | 第4章 　Jail子系统 | 下一页

# 4.2 系统对被囚禁程序的限制

在整个内核中，有一系列对被囚禁程序的约束措施。 通常，这些约束只对被囚禁的程序有效。如果这些程序试图突破这些约束， 相关的函数将出错返回。例如:

    if (jailed(td->td_ucred))
        return EPERM;

## 4.2.1 SysV进程间通信(IPC)

System V 进程间通信 (IPC) 是通过消息实现的。 每个进程都可以向其它进程发送消息， 告诉对方该做什么。 处理消息的函数是： msgctl(3)、msgget(3)、msgsnd(3) 和 msgrcv(3)。前面已经提到，一些 sysctl 开关可以影响 jail 的行为， 其中有一个是 `security.jail.sysvipc_allowed`。 在大多数系统上， 这个 sysctl 项会设成0。 如果将它设为1， 则会完全失去 jail 的意义： 因为那样在 jail 中特权进程就可以影响被监禁的环境外的进程了。 消息与信号的区别是：消息仅由一个信号编号组成。

`/usr/src/sys/kern/sysv_msg.c`:

- `msgget(key, msgflg)`: `msgget`返回(也可能创建)一个消息描述符， 以指派一个在其它函数中使用的消息队列。

- `msgctl(msgid, cmd, buf)`: 通过这个函数， 一个进程可以查询一个消息描述符的状态。

- `msgsnd(msgid, msgp, msgsz, msgflg)`: `msgsnd`向一个进程发送一条消息。

- `msgrcv(msgid, msgp, msgsz, msgtyp, msgflg)`: 进程用这个函数接收消息。

在这些函数对应的系统调用的代码中，都有这样一个条件判断：

    /usr/src/sys/kern/sysv_msg.c:
    if (!jail_sysvipc_allowed && jailed(td->td_ucred))
        return (ENOSYS);

信号量系统调用使得进程可以通过一系列原子操作实现同步。 信号量为进程锁定资源提供了又一种途径。 然而，进程将为正在被使用的信号量进入等待状态，一直休眠到资源被释放。 在jail中如下的信号量系统调用将会失效: semget(2), semctl(2) 和semop(2)。

`/usr/src/sys/kern/sysv_sem.c`:

- `semctl(semid, num, cmd, ...)`: `semctl`对在信号量队列中用`semid`标识的信号量执行`cmd`指定的命令。

- `semget(key, nsems, flag)`: `semget`建立一个对应于`key`的信号量数组。

  `参数key和flag与他们在msgget()的意义相同。`

- `setop(semid, array, nops)`: `semop`对semid标识的信号量完成一组由array所指定的操作。

System V IPC使进程间可以共享内存。进程之间可以通过它们虚拟地址空间 的共享部分以及相关数据读写操作直接通讯。这些系统调用在被监禁的环境中将会失效: shmdt(2)、shmat(2)、shmctl(2)和shmget(2)

`/usr/src/sys/kern/sysv_shm.c`:

- `shmctl(shmid, cmd, buf)`: `shmctl`对`id`标识的共享内存区域做各种各样的控制。

- `shmget(key, size, flag)`: `shmget`建立/打开`size`字节的共享内存区域。

- `shmat(shmid, addr, flag)`: `shmat`将`shmid`标识的共享内存区域指派到进程的地址空间里。

- `shmdt(addr)`: `shmdt`取消共享内存区域的地址指派。

## 4.2.2 套接字

Jail以一种特殊的方式处理socket(2)系统调用和相关的低级套接字函数。 为了决定一个套接字是否允许被创建，它先检查sysctl项 `security.jail.socket_unixiproute_only`是否被设置为1。 如果被设为1，套接字建立时将只能指定这些协议族： `PF_LOCAL`, `PF_INET`, `PF_ROUTE`。否则，socket(2)将会返回出错。

    /usr/src/sys/kern/uipc_socket.c:
    int
    socreate(int dom, struct socket **aso, int type, int proto,
        struct ucred *cred, struct thread *td)
    {
        struct protosw *prp;
    ...
        if (jailed(cred) && jail_socket_unixiproute_only &&
            prp->pr_domain->dom_family != PF_LOCAL &&
            prp->pr_domain->dom_family != PF_INET &&
            prp->pr_domain->dom_family != PF_ROUTE) {
            return (EPROTONOSUPPORT);
        }
    ...
    }

## 4.2.3 Berkeley包过滤器

Berkeley包过滤器提供了一个与协议无关的，直接通向数据链路层的低级接口。 现在BPF是否可以在监禁的环境中被使用是通过devfs(8)来控制的。

## 4.2.4 网络协议

网络协议TCP, UDP, IP和ICMP很常见。IP和ICMP处于同一协议层次：第二层， 网络层。当参数`nam`被设置时， 有一些限制措施会防止被囚禁的程序绑定到一些网络接口上。 `nam`是一个指向`sockaddr`结构体的指针， 描述可以绑定服务的地址。一个更确切的定义：`sockaddr`“是一个模板，包含了地址的标识符和地址的长度”。 在函数`in_pcbbind_setup()`中`sin`是一个指向`sockaddr_in`结构体的指针， 这个结构体包含了套接字可以绑定的端口、地址、长度、协议族。 这就禁止了在jail中的进程指定不属于这个进程所存在于的jail的IP地址。

    /usr/src/sys/kern/netinet/in_pcb.c:
    int
    in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
        u_short *lportp, struct ucred *cred)
    {
        ...
        struct sockaddr_in *sin;
        ...
        if (nam) {
            sin = (struct sockaddr_in *)nam;
            ...
            if (sin->sin_addr.s_addr != INADDR_ANY)
                if (prison_ip(cred, 0, &sin->sin_addr.s_addr))
                    return(EINVAL);
            ...
            if (lport) {
                ...
                if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr))
                    return (EADDRNOTAVAIL);
                ...
            }
        }
        if (lport == 0) {
            ...
            if (laddr.s_addr != INADDR_ANY)
                if (prison_ip(cred, 0, &laddr.s_addr))
                    return (EINVAL);
            ...
        }
    ...
        if (prison_ip(cred, 0, &laddr.s_addr))
            return (EINVAL);
    ...
    }

你也许想知道函数`prison_ip()`做什么。 `prison_ip()`有三个参数，一个指向身份凭证的指针(用`cred`表示)， 一些标志和一个IP地址。当这个IP地址不属于这个jail时，返回1； 否则返回0。正如你从代码中看见的，如果，那个IP地址确实不属于这个jail， 就不再允许向这个网络地址绑定协议。

    /usr/src/sys/kern/kern_jail.c:
    int
    prison_ip(struct ucred *cred, int flag, u_int32_t *ip)
    {
        u_int32_t tmp;

        if (!jailed(cred))
            return (0);
        if (flag)
            tmp = *ip;
        else
            tmp = ntohl(*ip);
        if (tmp == INADDR_ANY) {
            if (flag)
                *ip = cred->cr_prison->pr_ip;
            else
                *ip = htonl(cred->cr_prison->pr_ip);
            return (0);
        }
        if (tmp == INADDR_LOOPBACK) {
            if (flag)
                *ip = cred->cr_prison->pr_ip;
            else
                *ip = htonl(cred->cr_prison->pr_ip);
            return (0);
        }
        if (cred->cr_prison->pr_ip != tmp)
            return (1);
        return (0);
    }

## 4.2.5 文件系统

如果完全级别大于0，即便是jail里面的`root`， 也不允许在Jail中取消或更改文件标志，如“不可修改”、“只可添加”、“不可删除”标志。

    /usr/src/sys/ufs/ufs/ufs_vnops.c:
    static int
    ufs_setattr(ap)
        ...
    {
        ...
            if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
                if (ip->i_flags
                    & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
                        error = securelevel_gt(cred, 0);
                        if (error)
                            return (error);
                }
                ...
            }
    }
    /usr/src/sys/kern/kern_priv.c
    int
    priv_check_cred(struct ucred *cred, int priv, int flags)
    {
        ...
        error = prison_priv_check(cred, priv);
        if (error)
            return (error);
        ...
    }
    /usr/src/sys/kern/kern_jail.c
    int
    prison_priv_check(struct ucred *cred, int priv)
    {
        ...
        switch (priv) {
        ...
        case PRIV_VFS_SYSFLAGS:
            if (jail_chflags_allowed)
                return (0);
            else
                return (EPERM);
        ...
        }
        ...
    }

上一页 | 起始页 | 下一页
Jail子系统 | 上一级 | SYSINIT框架

本文档和其它文档可从这里下载：ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题，请先阅读文档，如不能解决再联系<questions@FreeBSD.org>.
关于本文档的问题请发信联系 <doc@FreeBSD.org>.