У меня есть проект, целью которого является запуск php-cgi chrooted для массового виртуального хостинга (более 10 тыс. Виртуальных хостов), причем каждый виртуальный хост имеет свой собственный chroot под Ubuntu Lucid x86_64.
Я бы хотел избежать создания необходимой среды внутри каждого chroot для таких вещей, как / dev / null, / dev / zero, locales, icons ... и всего, что может понадобиться модулям php, считая, что они работают вне chroot.
Цель состоит в том, чтобы php-cgi работал внутри chroot, но предоставил ему доступ к файлам вне chroot , если эти файлы (для большинства из них) открыты в режиме чтения.только режим и в разрешенном списке (/ dev / log, / dev / zero, / dev / null, путь к локали ...)
Кажется очевидным, что способ создать (или использовать, если он существует)) модуль ядра, который может перехватывать и перенаправлять доверенные пути open () вне chroot.Но я не думаю, что это самый простой способ:
- Я никогда не делал модуль ядра, поэтому неправильно оцениваю сложность.
- Кажется, что есть несколько системных вызововперехватить файл "open" (open, connect, mmap ...), но я думаю, что есть общая функция ядра для всего, что связано с открытием файла.
Я хочу минимизировать количествопатчи к php или его модулю, чтобы свести к минимуму объем работы, необходимый каждый раз, когда я буду обновлять нашу платформу до последней стабильной версии PHP (и, таким образом, обновлять вышедшие версии PHP чаще и быстрее), поэтому я считаю, что лучше исправлять поведениеPHP извне (поскольку у нас есть особые настройки, поэтому исправление PHP и предложение исправления для upstream не имеет значения).
Вместо этого я сейчас пытаюсь найти решение для пользовательского пространства: перехватить функции libc с помощью LD_PRELOAD, который работаетв большинстве случаев это действительно быстро реализуется, но я столкнулся с проблемой, которую не могу решить в одиночку.(Идея состоит в том, чтобы поговорить с демоном, работающим вне chroot, и получить от него дескриптор файла, используя ioctl SENDFD и RECVFD).
Когда я вызываю syslog () (сначала без openlog ()), syslog () вызывает метод connect (), чтобы открыть файл .
Пример:
folays@phenix:~/ldpreload$ strace logger test 2>&1 | grep connect
connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(3, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(1, {sa_family=AF_FILE, path="/dev/log"}, 110) = 0
Пока все хорошо, я пытался подключить функцию connect () в libc, безуспешно.Я также попытался установить некоторые флаги для dlopen () внутри функции _init () моей библиотеки предварительной загрузки, чтобы проверить, могут ли некоторые из них выполнить эту работу без успеха
Вот соответствующий код моей предварительной загрузкибиблиотека:
void __attribute__((constructor)) my_init(void)
{
printf("INIT preloadz %s\n", __progname);
dlopen(getenv("LD_PRELOAD"), RTLD_NOLOAD | RTLD_DEEPBIND | RTLD_GLOBAL |
RTLD_NOW);
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
printf("HOOKED connect\n");
int (*f)() = dlsym(RTLD_NEXT, "connect");
int ret = f(sockfd, addr, addrlen);
return ret;
}
int __connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
printf("HOOKED __connect\n");
int (*f)() = dlsym(RTLD_NEXT, "connect");
int ret = f(sockfd, addr, addrlen);
return ret;
}
Но функция connect () в libc по-прежнему имеет приоритет над моей:
folays@phenix:~/ldpreload$ LD_PRELOAD=./lib-preload.so logger test
INIT preloadz logger
[...] no lines with "HOOKED connect..." [...]
folays@phenix:~/ldpreload$
Глядя на код syslog () (apt-get source libc6,glibc-2.13 / misc / syslog.c), кажется, он вызывает openlog_internal, который, в свою очередь, вызывает __connect (), в строке 386 misc / syslog.c:
if (LogFile != -1 && !connected)
{
int old_errno = errno;
if (__connect(LogFile, &SyslogAddr, sizeof(SyslogAddr))
== -1)
{
Ну, objdump показывает мне соединение и__connect в таблице динамических символов libc:
folays@phenix:~/ldpreload$ objdump -T /lib/x86_64-linux-gnu/libc.so.6 |grep -i connec
00000000000e6d00 w DF .text 000000000000005e GLIBC_2.2.5 connect
00000000000e6d00 w DF .text 000000000000005e GLIBC_2.2.5 __connect
Но в записях динамического перемещения нет символа connect, поэтому я предполагаю, что это объясняет, почему я не могу успешно переопределить метод connect (), используемый openlog_internal (), онвероятно, не использует динамическое перемещение символов и, вероятно, имеет адрес функции __connect () в hard (относительное смещение -fPIC?).
folays@phenix:~/ldpreload$ objdump -R /lib/x86_64-linux-gnu/libc.so.6 |grep -i connec
folays@phenix:~/ldpreload$
connect - это слабый псевдоним __connect:
eglibc-2.13/socket/connect.c:weak_alias (__connect, connect)
gdb по-прежнему может использовать точку останова на символе libc connect библиотеки libc:
folays@phenix:~/ldpreload$ gdb logger
(gdb) b connect
Breakpoint 1 at 0x400dc8
(gdb) r test
Starting program: /usr/bin/logger
Breakpoint 1, connect () at ../sysdeps/unix/syscall-template.S:82
82 ../sysdeps/unix/syscall-template.S: No such file or directory.
in ../sysdeps/unix/syscall-template.S
(gdb) c 2
Will ignore next crossing of breakpoint 1. Continuing.
Breakpoint 1, connect () at ../sysdeps/unix/syscall-template.S:82
82 in ../sysdeps/unix/syscall-template.S
(gdb) bt
#0 connect () at ../sysdeps/unix/syscall-template.S:82
#1 0x00007ffff7b28974 in openlog_internal (ident=<value optimized out>, logstat=<value optimized out>, logfac=<value optimized out>) at ../misc/syslog.c:386
#2 0x00007ffff7b29187 in __vsyslog_chk (pri=<value optimized out>, flag=1, fmt=0x40198e "%s", ap=0x7fffffffdd40) at ../misc/syslog.c:274
#3 0x00007ffff7b293af in __syslog_chk (pri=<value optimized out>, flag=<value optimized out>, fmt=<value optimized out>) at ../misc/syslog.c:131
Конечно, я мог бы полностью пропустить эту конкретную проблему, выполнив openlog () сам, но я думаю,что я столкнусь с тем же типом проблемы с некоторыми другими функциями.
Я не очень понимаю, почему openlog_internal не использует динамическое перемещение символов для вызова __connect (), и если вообще возможно перехватить это __connect () вызов с использованием простого механизма LD_PRELOAD.
Другие способы, которыми я вижу, как это можно сделать:
- Загрузите libc.so из LD_PRELOAD с помощью dlopen, получите адрес __connect библиотеки libc с помощью dlsym (), а затем исправьте функцию (в соответствии с ASM), чтобы заставить работать ловушку. Это кажется излишним и склонным к ошибкам.
- Используйте измененный пользовательский libc для PHP, чтобы исправить эти проблемы непосредственно у источника (функции open / connect / mmap ...)
- Код LKM, чтобы перенаправить доступ к файлу, где я хочу. Плюсы: нет необходимости в ioctl (SENDFD) и нет демона вне chroot.
Я был бы очень признателен, если бы это было возможно, узнать, как я мог бы перехватить вызов __connect (), выданный openlog_internal, предложениями или ссылками на документацию ядра, связанную с перехватом syscall и перенаправлением.
Мой поиск в Google, связанный с "системными вызовами хуков", обнаружил множество ссылок на LSM , но, похоже, он позволяет только спискам ACL отвечать "да" или "нет", но перенаправления путей open () нет.
Спасибо за чтение.