Interested Article - Seccomp

seccomp (сокр. от англ. secure computing mode ) — один из механизмов безопасности ядра Linux , который обеспечивает возможность ограничивать набор доступных системных вызовов для приложений, а также с помощью механизма BPF ( Berkeley Packet Filter ) производить сложную фильтрацию вызовов и их аргументов. Впервые появился в ядре версии 2.6.12 в 2005 году .

Для работы с недоверенными или непроверенными, а поэтому потенциально опасными программами желательно использовать специально выделенные среды, из которых нельзя нанести вред работоспособности системы в целом. В таких средах (песочницах, контейнерах) для запускаемых программ лимитированы многие системные возможности, такие как доступ к сети, устройствам ввода-вывода, взаимодействие с операционной системой. Механизм seccomp определяет для процесса набор разрешённых системных вызовов и блокирует те, которые не были заранее объявлены. В настоящее время используется в ряде браузеров , Linux подобных ОС и некоторых системах виртуализации .

История

Механизм seccomp был разработан Андреа Арканджели ( итал. Andrea Arcangeli ) в рамках коммерческого проекта CPUShare, основной идеей которого было определение возможностей и наработка решений по предоставлению вычислительных ресурсов компьютеров, в частности с ОС Linux, для исполнения стороннего кода. Впервые появился в ядре Linux в марте 2005 года (версия 2.6.12). В первой версии чтобы включить seccomp, процесс был должен записать «1» в файл /proc/PID/seccomp. После этого гостевому коду были доступны только четыре системных вызова: read() , write() , exit() и sigreturn() . В 2007 году в версии 2.6.23 была добавлена возможность включения seccomp через системный вызов prctl() с помощью операции PR_SET_SECCOMP, а интерфейс взаимодействия через /proc был исключён .

Несмотря на то, что проект CPUShare, для которого seccomp разрабатывался, не получил развития и закрылся, он остался в ядре Linux . В феврале 2009 года в коде seccomp обнаружилась уязвимость. Она была связана с тем, что в 32-разрядной и 64-разрядной версиях Linux одним и тем же номерам соответствовали разные системные вызовы. Например, разрешённому вызову read() из 64-битной версии соответствовал запрещённый вызов restart_syscall() из 32-битной версии. После этого встал вопрос об исключении seccomp из Linux, а Линус Торвальдс предположил, что seccomp никем не используется .

Поскольку seccomp был достаточно прост для использования, у разработчиков Google Chrome возникла идея реализовать на его основе средство для запуска сторонних плагинов . Однако для реализации этой задачи простого блокирования всех системных вызовов за исключением четырёх разрешённых было недостаточно. В итоге в 2012 году с seccomp был интегрирован механизм BPF (Berkeley Packet Filter) (в версии 3.5 добавлен второй режим работы — SECCOMP_MODE_FILTER), значительно расширивший возможности механизма — после нововведения гостевой процесс мог более гибко выбирать набор разрешённых и запрещённых системных вызовов, присоединяя соответствующую BPF-программу. В 2012 году появилась также библиотека libseccomp, которая предоставляет собой простой и удобный API для фильтрации системных вызовов .

Для упрощения использования seccomp, в 2013 году в версии 3.8 было добавлено поле «Seccomp» в /proc/PID/status, которое позволяет выяснить состояние seccomp (после того, как включение убрали из /proc, узнать, работает ли уже включённый seccomp было невозможно без завершения процесса). В 2014 году в версии 3.17 добавлен специальный системный вызов — seccomp() , который частично повторяет функциональность prctl() .

В ноябре 2017 года с выходом библиотеки glibc версии 2.26 появилась новая проблема: поскольку в программах на языке C системные вызовы делаются не напрямую, а через обёртки стандартной библиотеки, названия которых могут не совпадать с названиями системных вызовов, запрет какого-либо вызова может неожиданным образом повлиять на программу. Например, функция open() из стандартной библиотеки glibc версии 2.26 реализована через системный вызов openat() , который не совпадает с вызовом open() и может быть ошибочно заблокирован автором фильтра .

Описание

Чтобы включить в программе seccomp, можно воспользоваться системным вызовом seccomp() или prctl() . На данный момент существуют два режима работы seccomp: SECCOMP_MODE_STRICT и SECCOMP_MODE_FILTER. Узнать, включён ли seccomp и в каком режиме, можно из поля «Seccomp» в файла /proc/[pid]/status. Поле принимает значения 0, 1 или 2: 0 — seccomp для процесса не включён, 1 — seccomp в режиме SECCOMP_MODE_STRICT, 2 — в режиме SECCOMP_MODE_FILTER . Включить seccomp для других процессов нельзя .

Режим SECCOMP_MODE_STRICT

Был единственным режимом работы до Linux 3.5. Чтобы его можно было использовать, ядро должно быть сконфигурировано с ключом CONFIG_SECCOMP=y .

Чтобы войти в этот режим, необходимо сделать вызов prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) или, что то же самое, seccomp(SECCOMP_SET_MODE_STRICT, 0, NULL) . Теперь процесс может использовать только четыре системных вызова: read() , write() , _exit() и sigreturn() . Результатом использования других системных вызовов будет прерывание процесса сигналом SIGKILL. Поскольку системный вызов open() запрещён, если потребуется проверка включения seccomp, файл status из procfs необходимо открыть с правами на чтение до включения seccomp в процессе .

Режим SECCOMP_MODE_FILTER

Режим работы, который появился в ядре Linux версии 3.5 . Доступен, если при сборке ядра был установлен флаг CONFIG_SECCOMP_FILTER=y.

Перед тем, как переходить в этот режим, нужно выполнить вызов prctl(PR_SET_NO_NEW_PRIVS, 1) и, таким образом, установить для процесса бит no_new_privs. Это обязательное требование объясняется тем, что иначе непривилегированный процесс может выполнить привилегированную программу с помощью execve() . Если этого не сделать, то переход в режим SECCOMP_MODE_FILTER не удастся .

После установки бита no_new_privs чтобы присоединить к процессу фильтр нужно выполнить вызов prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, args) или seccomp(SECCOMP_SET_MODE_FILTER, 0, args) , где args — указатель на структуру sock_fprog , которая из состоит массива BPF-инструкций и его длины. Фильтр запускается каждый раз, когда процесс совершает системный вызов. При запуске фильтр получает на вход структуру с данными о номере системного вызова, архитектуре, текущем состоянии счётчика команд и аргументах вызова . Фильтр обязательно должен возвратить 32-битное значение, в котором верхние 16 бит содержат код действия, которое будет произведено, а нижние 16 бит содержат данные. Если в алгоритме фильтра по ошибке не предусмотрен возврат значения, то он не пройдет статический анализ и такой фильтр присоединён не будет .

Если к процессу присоединено несколько фильтров, из них будет сформирован односвязный список и они будут запущены в порядке, обратном порядку их добавления, даже если один из фильтров должен убить процесс . В таком случае, возвращаемое значение системного вызова будет определяться первым (по порядку запуска фильтров) возвращаемым значением с наивысшим приоритетом, в соответствии с таблицей (в порядке уменьшения приоритета) :

Действие Результат
SECCOMP_RET_KILL Системный вызов не выполняется, процесс будет убит с помощью сигнала SIGSYS
SECCOMP_RET_TRAP Системный вызов не выполняется, процесс получает сигнал SIGSYS, обработчику сигнала доступна информация о системном вызове, который привёл к этому результату
SECCOMP_RET_ERRNO Системный вызов не выполняется, в переменной errno находится возвращаемое значение фильтра
SECCOMP_RET_TRACE Если с помощью ptrace() для процесса указан трассировщик, то он будет уведомлён о вызове. Если не указан, то системный вызов не выполняется
SECCOMP_RET_ALLOW Системный вызов выполняется

Примеры использования

Включение режима SECCOMP_MODE_STRICT

# include <stdio.h>
# include <unistd.h>
# include <linux/seccomp.h>
# include <sys/prctl.h>
# include <fcntl.h>

int main () {
    int fd; 

    prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);

    fprintf(stderr, "try open\n");
    fd = open("test_file", O_CREAT);
    fprintf(stderr, "fd = %d", fd);

    return 0;
}

Результат работы:

$ gcc test_seccomp.c -o test_seccomp
$ ./test_seccomp
try open
Killed

В приведённом примере после вызова prctl() процесс работает ровно до момента, когда попытается сделать вызов open() .

Примеры использования в ПО

  • Операционная система Chrome OS
  • Операционная система Android Oreo
  • Браузер Firefox
  • FTP-сервер vsftpd
  • Flatpak — инструмент для распространения и установки приложений внутри контейнеров
  • Система виртуализации Docker
  • Браузер с открытым исходным кодом Chromium
  • Система контейнерной виртуализации LXC

Примечания

  1. .
  2. .
  3. .
  4. .
  5. Jonathan Corbet. // LWN.net. — 2009. — 13 мая. 12 ноября 2017 года.
  6. Jonathan Corbet. // LWN.net. — 2017. — 1 ноября. 9 декабря 2017 года.
  7. .
  8. .
  9. .
  10. . The Linux Kernel Archives . Linux Kernel Organization. Дата обращения: 3 марта 2018. 13 октября 2017 года.
  11. .
  12. .
  13. .

Литература

  • Steven McCanne, Van Jacobson. // 1993 Winter USENIX. — San Diego, CA, 1993. — 2 января.
  • Greg Kroah-Hartman. Linux Kernel in a Nutshell. — 1005 Gravenstein Highway North, Sebastopol, CA 95472: O'Reilly Media, 2007. — 208 с. — ISBN 978-0596100797 .
  • Michael Kerrisk. The Linux Programming Interface. — San Francisco: No Starch Press, 2010. — 1556 с. — ISBN 978-1-59327-220-3 .
  • Jake Edge, Michael Kerrisk. // LWN.net : Linux Plumbers Conference. — Seattle, Washington, USA, 2015. — 1 сентября.
  • Taesoo Kim, Nickolai Zeldovich. // 2013 USENIX Annual Technical Conference. — 2013.
  • Ma Bo, Mu Dejun, Fan Wei, Hu Wei. (англ.) // IEEE. — 2013. — 01 Июль. — doi : .
  • Imamjafar Borate, Chavan R. K. (англ.) // International Journal of Computer Applications. — 2016. — Август ( т. 148 , № 8 ).
  • Senthil Kumaran S. Practical LXC and LXD: Linux Containers for Virtualization and Orchestration. — Apress, 2017. — С. 147. — 159 с. — ISBN 978-1-4842-3024-4 .
  • Adrian Mouat. Using Docker: Developing and Deploying Software with Containers. — 1005 Gravenstein Highway North, Sebastopol, CA 95472: O'Reilly Media, 2015. — С. 320. — 354 с.
  • Kurt Dietrich, Johannes Winter. // Trust and Trustworthy Computing: 4th International Conference. — Pittsburgh, PA, USA: Springer, 2011. — Июнь.
  • Anto.Y. Chrome OS and Secret of Google. — Lambert Academic Publishing, 2012. — С. 41. — 228 с. — ISBN 978-3-659-17127-7 .
  • Lingguang Lei, Jianhua Sun, Kun Sun, Chris Shenefiel, Rui Ma, Yuewu Wang, Qi Li. // Detection of Intrusions and Malware, and Vulnerability Assessment: 14th International Conference, DIMVA 2017. — Bonn, Germany: Springer, 2017. — 1 июля.

Ссылки

  • Michael Kerrisk. . Michael Kerrisk. — пример использования seccomp из The Linux Programming Interface. (англ.)
  • . The Linux Kernel Archives . Linux Kernel Organization. Дата обращения: 3 марта 2018. часть документации ядра Linux kernel (англ.)
  • Julien Tinnes, Chris Evans. // HITB Malaysia. — 2009. — Октябрь. (англ.)
  • Jake Edge. . Eklektix, Inc. (2 сентября 2015). (англ.)
  • Jonathan Corbet. . Eklektix, Inc. (13 мая 2009). (англ.)


Источник —

Same as Seccomp