Можно ли подделать конкретный путь для процесса?

9

Я пытаюсь запустить ADB на сервере Linux с несколькими пользователями, где я не являюсь пользователем root (чтобы играть с моим эмулятором Android). Демон adb записывает свои логи в файл, /tmp/adb.logкоторый, к сожалению, жестко запрограммирован в ADB, и эта ситуация не изменится .

Таким образом, ADB не в состоянии бежать, давая очевидную ошибку: cannot open '/tmp/adb.log': Permission denied. Этот файл создан другим пользователем и /tmpимеет прикрепленный бит. Если я начну adb с adb nodaemon serverзаписи в stdout, ошибок не будет (я также установил для его порта уникальное значение, чтобы избежать конфликтов).

Мой вопрос: есть ли способ заставить ADB записать в другой файл, чем /tmp/adb.log? В более общем смысле, есть ли способ создать своего рода символьную ссылку для конкретного процесса? Я хочу перенаправить все обращения к файлам /tmp/adb.log, скажем, к файлу ~/tmp/adb.log.

Опять же , я не корень на сервере, поэтому chroot, mount -o rbindи chmodне являются допустимыми вариантами. Если возможно, я бы не хотел изменять источники ADB, но, конечно, если других решений нет, я сделаю это.

PS Для конкретного случая ADB я могу прибегнуть к работе adb nodaemon serverс nohupперенаправлением вывода и выводу, но общий вопрос все еще актуален.

gluk47
источник
2
да. Вы можете поместить свой процесс в закрытое пространство имен монтирования и смонтировать какой-либо другой файл поверх /tmp/adb.log, или даже смонтировать его собственный приватный файл /tmp. делай man unshareи man namespacesи man nsenter.
mikeserv
1
@mikeserv здорово, похоже, это именно то, что мне нужно, спасибо! Если вы переформатируете свой комментарий как ответ, я смогу сделать его принятым.
gluk47
Или есть LD_PRELOADхитрости, хотя это будет сложнее.
15:47
@ thrig да, я хоть о LD_PRELOAD, но, честно говоря, было бы проще жестко закодировать /home/$USER/tmp/adb.logи перестроить adb :)
gluk47

Ответы:

5

Вот очень простой пример использования util-linux's', unshareчтобы поместить процесс в приватное пространство имен монтирования и дать ему другое представление о той же файловой системе, которую имеет его родительский объект:

{   cd /tmp                      #usually a safe place for this stuff
    echo hey   >file             #some
    echo there >file2            #evidence
    sudo unshare -m sh -c '      #unshare requires root by default
         mount -B file2 file     #bind mount there over hey
         cat file                #show it
         kill -TSTP "$$"         #suspend root shell and switch back to parent
         umount file             #unbind there
         cat file'               #show it
    cat file                     #root shell just suspended
    fg                           #bring it back
    cat file2                    #round it off
}

there                            #root shell
hey                              #root shell suspended
hey                              #root shell restored
there                            #rounded

Вы можете предоставить процессу частное представление своей файловой системы с помощью unshareутилиты в современных системах linux, хотя сама функция пространства имен mount была достаточно зрелой для всей серии ядер 3.x. Вы можете ввести ранее существующие пространства имен всех видов с помощью nsenterутилиты из одного пакета, и вы можете узнать больше с помощью man.

mikeserv
источник
Только один вопрос: это я или это идеальное решение, но только для пользователя root?
gluk47
@ gluk47 - так не должно быть. Вы можете unshareвсе виды пространств имен - чтобы включить пространство имен пользователя. и поэтому ваш пользователь может запустить пространство имен, в котором у него есть root-доступ, и все, что он делает внутри, что может испортить корневой пользователь, не влияет на родительское пространство имен. другими словами, пространство имен монтирования может быть встроено в пространство имен пользователя. вам действительно нужно прочитать эти manстраницы. это становится глубоким. это именно так dockerи sytemd-nspawnработает.
mikeserv
Я прочитал эти справочные страницы и примеры из Интернета) Кажется, мне нужно больше их читать, просто спасибо, что указали на эту технологию, я почему-то вообще не знал об этом.
gluk47
@ gluk47 - не принимай ответы ради верности. в то время как чувство ценится, такие вещи побеждают цель этого места. принять ответ, который вы используете . если этот не тот, пожалуйста, не принимайте этот ответ. кстати, если процесс запускается как root, это не значит, что он должен оставаться корневым процессом. есть runuserутилита, с которой можно работать unshare, и если вы все равно unshare()готовы писать скомпилированные программы, нет никаких причин, по которым вы не могли бы использовать syscall для того же или даже просто system()с suid-двоичным файлом.
mikeserv
Я бы точно не принял ответ, если бы он не был полезен. Я нахожу оба ответа актуальными и полезными, поэтому единственная причина, по которой стоит проверить один из этих ответов,
мнение
11

LD_PRELOAD не слишком сложен, и вам не нужно быть пользователем root. Вставьте свою собственную подпрограмму C, которая вызывается вместо реальной open()в библиотеке C. Ваша процедура проверяет, является ли открываемый файл "/tmp/adb.log", и вызывает реальное открытие с другим именем файла. Вот ваш shim_open.c:

/*
 * capture calls to a routine and replace with your code
 * gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.c
 * LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log
 */
#define _FCNTL_H 1 /* hack for open() prototype */
#define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#define OLDNAME "/tmp/adb.log"
#define NEWNAME "/tmp/myadb.log"

int open(const char *pathname, int flags, mode_t mode){
    static int (*real_open)(const char *pathname, int flags, mode_t mode) = NULL;

    if (!real_open) {
        real_open = dlsym(RTLD_NEXT, "open");
        char *error = dlerror();
        if (error != NULL) {
            fprintf(stderr, "%s\n", error);
            exit(1);
        }
    }
    if (strcmp(pathname,OLDNAME)==0) pathname = NEWNAME;
    fprintf(stderr, "opening: %s\n", pathname);
    return real_open(pathname, flags, mode);
}

Скомпилируйте его gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.cи протестируйте, /tmp/myadb.logзапустив и запустив LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log. Затем попробуйте LD_PRELOAD на ADB.

meuh
источник
Ну, действительно, ваше решение - единственное, что мне удалось заставить работать, не будучи пользователем root. Я не справился с unshare ( Operation not permitted). Я надеюсь, что этого openдостаточно для обработки, но, наконец, добавить unlinkэтот обработчик не сложно.
gluk47
Оо. Как жаль, что я не могу проверить два ответа. Я обещал mikeserv проверить его решение как ответ, и оно действительно жизнеспособное.
gluk47
2
ничего. Об этом я тоже узнал unshare, поэтому мы все выиграли!
Меу
Через некоторое время еще раз спасибо за образец LD_PRELOAD. Поскольку я попробовал ваш код, я использую LD_PRELOAD в различных ситуациях, когда я даже не думал об этом. Моя жизнь изменилась к лучшему :)
gluk47
2
@ gluk47 Вот что удивительного в Gnu / Linux: вам не нужно прекращать исследовать! Есть так много хороших вещей, чтобы открыть и поделиться.
meuh