Для реализации jail, во многих местах в коде ядра встречаются соответствующие проверки и ограничения. Как правило, большинство этих проверок сводятся к проверке, является ли данный процесс ``заключенным'' (jailed) и возврат кода ошибки, если да. К примеру:
if (p->p_prison) return EPERM;
Подсистема межпроцессного взаимодействия SysV основана на сообщениях. Процессы могут посылать сообщения друг другу с различной информацией. Для отсылки сообщений используются следующие функции: msgsys, msgctl, msgget, msgsend и msgrcv. Ранее я упоминал о том, что существует ряд параметров ядра, установка или сброс которых могут повлиять на поведение Jail. Одним из таких параметров является jail_sysvipc_allowed. На большинстве систем этот параметр установлен в 0. Если его установить в 1, это может свести на нет само использование Jail, так как привилегированный пользователь внутри jail сможет посылать сообщения процессам вне jail, и таким образом, влиять на них. Отличие между сигналом и сообщением SysV состоит в том, что сигнал можно рассматривать как сообщение с одним полем данных, а именно, номером сигнала, когда как сообщения SysV могут содержать большее количество полей данных:
/usr/src/sys/kern/sysv_msg.c:
msgget(3): msgget возвращает (и, возможно, создает) дескриптор очереди сообщений для дальнейшей отсылки/приема сообщений. Очередь сообщений, где хранятся сообщения, посланные процессами, располагается в пространстве ядра.
msgctl(3): Используя эту функцию, процесс может запросить текущий статус очереди сообщений по ее дескриптору.
msgsnd(3): msgsnd отсылает сообщение в очередь сообщений.
msgrcv(3): Процесс вызывает эту функцию для извлечения сообщения из очереди сообщений
Для каждого из этих системных вызовов, в коде присутствует следующая проверка:
/usr/src/sys/kern/sysv msg.c: if (!jail.sysvipc.allowed && p->p_prison != NULL) return (ENOSYS);
Системные вызовы, связанные с семафорами, позволяют различным процессам синхронизировать выполнения путем выполнения атомарных операций над специальными объектами - семафорами. Семафоры - один из способов блокировки ресурсов. Процесс, ждущий освобождения семафора, будет находиться в состоянии сна до тех пор, пока семафор не будет освобожден. Для процесса в jail недоступны следующие вызовы: semsys, semget, semctl и semop.
/usr/src/sys/kern/sysv_sem.c:
semctl(2)(id, num, cmd, arg): Semctl выполняет операцию cmd над семафором, указанном id.
semget(2)(key, nsems, flag): Semget создает массив семафоров для ключа key.
Key и flag имеют то же значение, что и для вызова msgget.
semop(2)(id, ops, num): Semop производит атомарные операции, указанные op, над семафорами, указанными id.
SysV IPC позволяет процессам совместно использовать память. Процессы могут совместно использовать части своих адресных пространств и таким образом непосредственно обращаться к памяти другого процесса. Для процесса в jail недоступны следующие вызовы: shmdt, shmat, oshmctl, shmctl, shmget, и shmsys.
shmctl(2)(id, cmd, buf): shmctl производит различные управляющие операции над блоком памяти, указанном id.
shmget(2)(key, size, flag): shmget возвращает или создает новый блок памяти размером в size байт.
shmat(2)(id, addr, flag): shmat присоединяет блок памяти, указанным id к адресному пространству процесса.
shmdt(2)(addr): shmdt detaches отсоединяет блок памяти, ранее присоединенный по адресу addr.
Внутри jail системный вызов socket(2) и связанные с ним низкоуровневые функции работают особым образом. Для того чтобы определить, разрешено ли создание того или иного сокета, socket(2) сначала проверяет, установлен ли параметр ядра jail.socket.unixiproute.only. Если установлен, то сокет можно создать только для доменов PF_LOCAL, PF_INET или PF_ROUTE. Иначе, возвращается ошибка:
/usr/src/sys/kern/uipc_socket.c: int socreate(dom, aso, type, proto, p) ... register struct protosw *prp; ... { if (p->p_prison && jail_socket_unixiproute_only && prp->pr_domain->dom_family != PR_LOCAL && prp->pr_domain->dom_family != PF_INET && prp->pr_domain->dom_family != PF_ROUTE) return (EPROTONOSUPPORT); ... }
Berkeley Packet Filter обеспечивает интерфейс к уровню канала данных. Функция bpfopen() открывает устройство Ethernet. Процессам внутри jail вызов этой функции запрещен:
/usr/src/sys/net/bpf.c: static int bpfopen(dev, flags, fmt, p) ... { if (p->p_prison) return (EPERM); ... }
Существует ряд распространенных сетевых протоколов, таких как TCP, UDP, IP, ICMP. IP и ICMP находятся на одном уроне стека: на сетевом. Для предотвращения привязки jailed процесса к определенному порту, предусмотрен ряд мер: процессу позволено сделать bind(), только если установлен параметр nam. nam - это указатель на структуру sockaddr, которая указывает на адрес, к которому привязать сервис. В функции pcbbind, sin - это указатель на структуру sockaddr.in, содержащую порт, адрес и домен сокета, для которого требуется привязка.
/usr/src/sys/kern/netinet/in_pcb.c: int in.pcbbind(int, nam, p) ... struct sockaddr *nam; struct proc *p; { ... struct sockaddr.in *sin; ... if (nam) { sin = (struct sockaddr.in *)nam; ... if (sin->sin_addr.s_addr != INADDR_ANY) if (prison.ip(p, 0, &sin->sin.addr.s_addr)) return (EINVAL); .... } ... }
Вы можете спросить, что делает функция prison_ip(). prison.ip принимает три аргумента: текущий процесс (указываемый p), флаги, и ip адрес. Она возвращает 1 если ip адрес принадлежит jail и 0, если нет. Как можно видеть из фрагмента кода, если ip адрес принадлежит jail, сокету не будет позволено привязаться к порту.
/usr/src/sys/kern/kern_jail.c: int prison_ip(struct proc *p, int flag, u_int32_t *ip) { u_int32_t tmp; if (!p->p_prison) return (0); if (flag) tmp = *ip; else tmp = ntohl (*ip); if (tmp == INADDR_ANY) { if (flag) *ip = p->p_prison->pr_ip; else *ip = htonl(p->p_prison->pr_ip); return (0); } if (p->p_prison->pr_ip != tmp) return (1); return (0); }
Процессам в jail не позволено привязывать сервисы к ip адресам, не принадлежащим jail. Это ограничение также есть в функции in_pcbbind:
/usr/src/sys/net inet/in_pcb.c if (nam) { ... lport = sin->sin.port; ... if (lport) { ... if (p && p->p_prison) prison = 1; if (prison && prison_ip(p, 0, &sin->sin_addr.s_addr)) return (EADDRNOTAVAIL);
Даже пользователь root внутри jail не может устанавливать специальные флаги для файла, такие как immutable, append, no unlink, если securelevel больше 0:
/usr/src/sys/ufs/ufs/ufs_vnops.c: int ufs.setattr(ap) ... { if ((cred->cr.uid == 0) && (p->prison == NULL)) { if ((ip->i_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) && securelevel > 0) return (EPERM); }