воскресенье, 9 ноября 2008 г.

Перехват функций glibc на примере fakeroot

Пакет fakeroot был создан специально для системы сборки Debian. Идея fakeroot в том, чтобы за счет перехвата функций glibc сделать (непривилегированную) среду в которой файловые операции выполняются так, как будто бы они выполнялись от привилегированного пользователя. В свою очередь это дает возможность создавать архивы с произвольными правами содержимых файлов.
Например, как будучи обычным пользователем создать архив в котором владелец архивных файлов является root? А в fakeroot это возможно.

peter@crashed:/tmp$ id
uid=1000(peter) gid=1000(peter) группы=20(dialout),24(cdrom),25(floppy),29(audio),60(games),100(users),1000(peter)
peter@crashed:/tmp$ > test.txt
peter@crashed:/tmp$ ls -la test.txt
-rw-r--r-- 1 peter peter 0 Ноя 9 14:35 test.txt
peter@crashed:/tmp$ fakeroot
root@crashed:/tmp# ls -la test.txt
-rw-r--r-- 1 root root 0 Ноя 9 14:35 test.txt

Чудеса. В данном случае мы видим, что из среды fakeroot мы видим не реальные права файла, а подмененные. Также, если создать файл в среде fakeroot, то реальные права будут принадлежать пользователю, но в среде fakeroot они будут казаться принадлежащими root. Хорошо -- мы уже догадались как это работает...


peter@crashed:/tmp$ fakeroot
root@crashed:/tmp# env | grep fake
LD_PRELOAD=libfakeroot-sysv.so
LD_LIBRARY_PATH=/usr/lib/libfakeroot:/usr/lib64/libfakeroot:/usr/lib32/libfakeroot


Итак, fakeroot перехватывает библиотечные вызовы, например stat, с помощью механизма LD_PRELOAD. Данный способ перехвата широко известен и заключается в том, что линкер загружает заданные в LD_PRELOAD (или в файле /etc/ld.preload) библиотеку(и) раньше других и если в библиотеке определены требуемые функции (в нашем случае функции glibc) то они заменяют собой функции оригинальной библиотеки. Конечно, данный механизм не работает для suid программ, иначе это было бы нарушением безопасности.

Но как вызвать оригинальную функцию glibc из подмененной? Код fakeroot показывает нам это:

void *get_libc(){
#ifndef RTLD_NEXT
void *lib=0;
if(!lib){.
lib= dlopen(LIBCPATH,RTLD_LAZY);
}
if (NULL==lib) {
fprintf(stderr, "Couldn't find libc at: %s\n", LIBCPATH);
abort();
}
return lib;
#else
return RTLD_NEXT;
#endif
}
/* .... */
*(next_wrap[i].doit)=dlsym(get_libc(), next_wrap[i].name);
/* ... */


Как видим, для получения символа из glibc мы либо непосредственно берем адрес модуля с помощью dlopen, или с помощью макроса RTLD_NEXT (если он доступен) берем адрес следующего модуля, и после этого берем адрес требуемой функции через dlsym. Затем можно вызвать функцию по указателю.

Макрос RTLD_NEXT определен в только если при сборке определен флаг _GNU_SOURCE. В качестве примера напишем перехват функции fopen.


#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
FILE *fopen(const char *path, const char *mode)
{
FILE *(*real_fopen)(const char *, const char *);
real_fopen = dlsym(RTLD_NEXT, "fopen");
fprintf(stderr,"fopen: %s %s\n", path, mode);
return real_fopen(path, mode);
}
peter@crashed:/tmp$ gcc -shared hack.c -o hack.so -ldl -lc
peter@crashed:/tmp$ LD_PRELOAD=./hack.so gcc hack.c
fopen: /tmp/ccKDaDBH.s w+b
fopen: hack.gcda r+b
fopen: /tmp/ccKDaDBH.s r
...


Но вернемся к fakeroot. Попробуем выполнить следующие действия:

peter@crashed:/tmp$ fakeroot
root@crashed:/tmp# mknod hda b 3 0
root@crashed:/tmp# ls -la hda
brw-r--r-- 1 root root 3, 0 Ноя 9 16:07 hda
root@crashed:/tmp# chown peter hda
root@crashed:/tmp# ls -la hda
brw-r--r-- 1 peter root 3, 0 Ноя 9 16:07 hda
root@crashed:/tmp# exit
exit
peter@crashed:/tmp$ ls -la hda
-rw-r--r-- 1 peter peter 0 Ноя 9 16:07 hda


Оказывается, не все так просто! fakeroot делает видимость выполнения запрещенных файловых операций практически не отличимой от роли настоящего root! Невозможно добиться такого эффекта только за счет подмены вызовов glibc, ведь библиотека инициализируется каждый раз во время загрузки очередной программы, а fakeroot каким-то образом "помнит" проделанные операции в пределах своей сессии...


peter@crashed:/tmp$ fakeroot
root@crashed:/tmp# ps ax | grep fake
3429 pts/0 S 0:00 /bin/sh /usr/bin/fakeroot
3437 ? Ss 0:00 /usr/bin/faked-sysv


Итак, fakeroot это не только библиотека, но и демон, поддерживающий хэш-таблицы выполненных файловых операций. Такой подход позволяет практически полностью виртуализировать файловые операции и решить довольно-таки утилитарную задачу Debian...

Комментариев нет:

Архив блога