In kern_jail.c, the following sysctls are defined:
/usr/src/sys/kern/kern_jail.c:
int jail_set_hostname_allowed = 1;
SYSCTL_INT(_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
&jail_set_hostname_allowed, 0,
"Processes in jail can set their hostnames");
int jail_socket_unixiproute_only = 1;
SYSCTL_INT(_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
&jail_socket_unixiproute_only, 0,
"Processes in jail are limited to creating UNIX/IPv4/route sockets only
");
int jail_sysvipc_allowed = 0;
SYSCTL_INT(_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
&jail_sysvipc_allowed, 0,
"Processes in jail can use System V IPC primitives");
Each of these sysctls can be accessed by the user through the sysctl program.
Throughout the kernel, these specific sysctls are recognized by their name. For example,
the name of the first sysctl is jail.set.hostname.allowed.
Like all system calls, the jail(2) system call
takes two arguments, struct proc *p and struct jail_args *uap. p is a pointer to
a proc structure which describes the calling process. In this context, uap is a pointer
to a structure which specifies the arguments given to jail(2) from the
userland program jail.c. When I described the userland program
before, you saw that the jail(2) system call
was given a jail structure as its own argument.
/usr/src/sys/kern/kern_jail.c:
int
jail(p, uap)
struct proc *p;
struct jail_args /* {
syscallarg(struct jail *) jail;
} */ *uap;
Therefore, uap->jail would access the jail structure
which was passed to the system call. Next, the system call copies the jail structure into
kernel space using the copyin() function. copyin() takes three arguments: the data which is to be copied into
kernel space, uap->jail, where to store it, j and the size of the storage. The jail structure uap->jail is copied into kernel space and stored in another
jail structure, j.
/usr/src/sys/kern/kern_jail.c:
error = copyin(uap->jail, &j, sizeof j);
There is another important structure defined in jail.h. It is the prison structure
(pr). The prison structure is used exclusively within kernel
space. The jail(2) system call
copies everything from the jail structure onto the prison structure. Here is the
definition of the prison structure.
/usr/include/sys/jail.h:
struct prison {
int pr_ref;
char pr_host[MAXHOSTNAMELEN];
u_int32_t pr_ip;
void *pr_linux;
};
The jail() system call then allocates memory for a pointer to a prison structure and
copies data between the two structures.
/usr/src/sys/kern/kern_jail.c:
MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK);
bzero((caddr_t)pr, sizeof *pr);
error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
if (error)
goto bail;
Finally, the jail system call chroots the path specified. The chroot function is given
two arguments. The first is p, which represents the calling process, the second is a
pointer to the structure chroot args. The structure chroot args contains the path which
is to be chrooted. As you can see, the path specified in the jail structure is copied to
the chroot args structure and used.
/usr/src/sys/kern/kern_jail.c:
ca.path = j.path;
error = chroot(p, &ca);
These next three lines in the source are very important, as they specify how the
kernel recognizes a process as jailed. Each process on a Unix system is described by its
own proc structure. You can see the whole proc structure in /usr/include/sys/proc.h. For example, the p argument in any system
call is actually a pointer to that process' proc structure, as stated before. The proc
structure contains nodes which can describe the owner's identity (p_cred), the process resource limits (p_limit), and so on. In the definition of the process structure,
there is a pointer to a prison structure. (p_prison).
/usr/include/sys/proc.h:
struct proc {
...
struct prison *p_prison;
...
};
In kern_jail.c, the function then copies the pr structure,
which is filled with all the information from the original jail structure, over to the
p->p_prison structure. It then does a bitwise OR of p->p_flag with the constant P_JAILED, meaning that the calling process is now recognized as
jailed. The parent process of each process, forked within the jail, is the program jail
itself, as it calls the jail(2) system call.
When the program is executed through execve, it inherits the properties of its parents
proc structure, therefore it has the p->p_flag set, and
the p->p_prison structure is filled.
/usr/src/sys/kern/kern_jail.c
p->p.prison = pr;
p->p.flag |= P.JAILED;
When a process is forked from a parent process, the fork(2) system call
deals differently with imprisoned processes. In the fork system call, there are two
pointers to a proc structure p1 and
p2. p1 points to the parent's proc structure and p2 points to the child's unfilled proc structure. After copying all relevant data between the
structures, fork(2) checks if the
structure p->p_prison is filled on p2. If it is, it increments the pr.ref
by one, and sets the p_flag to one on the child process.
/usr/src/sys/kern/kern_fork.c:
if (p2->p_prison) {
p2->p_prison->pr_ref++;
p2->p_flag |= P_JAILED;
}