12.2. Ограничения

Для реализации jail, во многих местах в коде ядра встречаются соответствующие проверки и ограничения. Как правило, большинство этих проверок сводятся к проверке, является ли данный процесс ``заключенным'' (jailed) и возврат кода ошибки, если да. К примеру:

    if (p->p_prison)
            return EPERM;

12.2.1. SysV IPC

Подсистема межпроцессного взаимодействия SysV основана на сообщениях. Процессы могут посылать сообщения друг другу с различной информацией. Для отсылки сообщений используются следующие функции: msgsys, msgctl, msgget, msgsend и msgrcv. Ранее я упоминал о том, что существует ряд параметров ядра, установка или сброс которых могут повлиять на поведение Jail. Одним из таких параметров является jail_sysvipc_allowed. На большинстве систем этот параметр установлен в 0. Если его установить в 1, это может свести на нет само использование Jail, так как привилегированный пользователь внутри jail сможет посылать сообщения процессам вне jail, и таким образом, влиять на них. Отличие между сигналом и сообщением SysV состоит в том, что сигнал можно рассматривать как сообщение с одним полем данных, а именно, номером сигнала, когда как сообщения SysV могут содержать большее количество полей данных:

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

Для каждого из этих системных вызовов, в коде присутствует следующая проверка:

    /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:

SysV IPC позволяет процессам совместно использовать память. Процессы могут совместно использовать части своих адресных пространств и таким образом непосредственно обращаться к памяти другого процесса. Для процесса в jail недоступны следующие вызовы: shmdt, shmat, oshmctl, shmctl, shmget, и shmsys.

12.2.2. Сокеты

Внутри 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);
    ...
    }

12.2.3. Berkeley Packet Filter

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);
    ...
    }

12.2.4. Протоколы

Существует ряд распространенных сетевых протоколов, таких как 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);

12.2.5. Файловая система

Даже пользователь 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);
    }