在整个内核中,有一系列对被囚禁程序的约束措施。 通常,这些约束只对被囚禁的程序有效。如果这些程序试图突破这些约束, 相关的函数将出错返回。例如:
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
取消共享内存区域的地址指派。
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);
}
...
}网络协议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);
}如果完全级别大于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);
...
}
...
}本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.
如果对于FreeBSD有问题,请先阅读
文档,如不能解决再联系
<questions@FreeBSD.org>.
关于本文档的问题请发信联系
<doc@FreeBSD.org>.