Czy można sfałszować określoną ścieżkę dla procesu?

9

Próbuję uruchomić ADB na serwerze Linux z wieloma użytkownikami, na których nie jestem rootem (aby grać z moim emulatorem Androida). Demon adb zapisuje swoje dzienniki do pliku, /tmp/adb.logktóry niestety wydaje się być zakodowany na stałe w ADB i ta sytuacja się nie zmieni .

Więc adb nie udaje się uruchomić, co daje oczywisty błąd: cannot open '/tmp/adb.log': Permission denied. Ten plik jest tworzony przez innego użytkownika i /tmpma trochę lepką wersję. Jeśli zacznę adb od zmuszenia adb nodaemon servergo do zapisu na standardowe wyjście, nie wystąpią żadne błędy (ustawiłem również jego port na unikalną wartość, aby uniknąć konfliktów).

Moje pytanie brzmi: czy jest jakiś sposób, aby ADB zapisał do innego pliku niż /tmp/adb.log? Mówiąc bardziej ogólnie, czy istnieje sposób na stworzenie pewnego rodzaju dowiązania symbolicznego specyficznego dla procesu? Chcę przekierować wszystkie dostępy do plików /tmp/adb.log, mówiąc, do pliku ~/tmp/adb.log.

Znowu, nie jestem root na serwerze, tak chroot, mount -o rbindi chmodnie są poprawne opcje. Jeśli to możliwe, nie chciałbym modyfikować źródeł ADB, ale na pewno, jeśli nie będzie innych rozwiązań, zrobię to.

PS W konkretnym przypadku ADB mogę skorzystać adb nodaemon serverz nohupprzekierowania i wyjścia, ale ogólne pytanie jest nadal aktualne.

gluk47
źródło
2
tak. możesz umieścić swój proces w prywatnej przestrzeni nazw montowania i zamontować inny plik /tmp/adb.log, a nawet zamontować własny prywatny plik /tmp. zrobić man unsharei man namespacesi man nsenter.
mikeserv
1
@mikeserv świetnie, wydaje się, że właśnie tego potrzebuję, dziękuję! Jeśli sformatujesz swój komentarz jako odpowiedź, będę mógł ustawić go jako zaakceptowany.
gluk47
Albo są LD_PRELOADsztuczki, choć byłoby to bardziej skomplikowane.
thrig
@ thrig tak, myślałem o LD_PRELOAD, ale szczerze mówiąc, łatwiej byłoby na stałe /home/$USER/tmp/adb.log
kodować

Odpowiedzi:

5

Oto bardzo prosty przykład użycia util-linux„y unshareumieścić proces w obszarze nazw prywatnej zamontować i nadać mu inny widok tego samego systemu plików, jego rodzic ma obecnie:

{   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

Możesz nadać procesowi prywatny widok jego systemu plików za pomocą unsharenarzędzia w aktualnych systemach Linux, chociaż samo narzędzie przestrzeni nazw montowania jest dość dojrzałe dla całej serii jądra 3.x. Możesz wprowadzić wcześniej istniejące przestrzenie nazw wszelkiego rodzaju za pomocą nsenternarzędzia z tego samego pakietu, i możesz dowiedzieć się więcej za pomocą man.

mikeserv
źródło
Tylko jedno pytanie: czy to ja, czy jest to idealne rozwiązanie, ale tylko dla użytkownika root?
gluk47,
@ gluk47 - nie musi tak być. możesz unsharewszelkiego rodzaju przestrzeni nazw - aby uwzględnić przestrzeń nazw użytkownika. a więc użytkownik może uruchomić przestrzeń nazw, w której ma dostęp do konta root, a wszystko, co zrobi to, co użytkownik root może spieprzyć, nie wpływa na nadrzędną przestrzeń nazw. innymi słowy, przestrzeń nazw montowania może być osadzona w przestrzeni nazw użytkownika. naprawdę musisz przeczytać te manstrony. robi się głęboki. tak właśnie działa dockeri sytemd-nspawndziała.
mikeserv
Przeczytałem te strony podręcznika i przykłady z Internetu). Wydaje się, że muszę je przeczytać jeszcze raz. Dziękuję za wskazanie tej technologii. Jakoś wcale jej nie znałem.
gluk47,
@ gluk47 - nie akceptuj odpowiedzi ze względu na lojalność. choć docenia się sentyment, tego rodzaju rzeczy nie spełniają celu tego miejsca. zaakceptuj odpowiedź, której używasz . jeśli to nie jest ta, proszę nie przyjmować tej odpowiedzi. nawiasem mówiąc, tylko dlatego, że proces jest uruchamiany jako root, nie oznacza, że ​​musi on pozostać procesem root. istnieje runusernarzędzie, z którego można korzystać unshare, a jeśli i tak jesteś otwarty na pisanie skompilowanych programów, nie ma powodu, dla którego nie mógłbyś użyć tego polecenia unshare()syscall, aby zrobić to samo, a nawet po prostu system()z suid binary.
mikeserv
Na pewno nie zaakceptuję odpowiedzi, jeśli nie będzie przydatna. Uważam, że zarówno odpowiedzi są odpowiednie i pomocne, więc sentyment jest jedynym wyróżniającym powodem, aby sprawdzić jedną z tych odpowiedzi :)
gluk47
11

LD_PRELOAD nie jest zbyt trudny i nie musisz być rootem. Wstaw swoją własną procedurę C, która jest wywoływana zamiast rzeczywistej open()w bibliotece C. Procedura sprawdza, czy plik do otwarcia to „/tmp/adb.log” i wywołuje rzeczywiste otwarcie z inną nazwą pliku. Oto twój 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);
}

Skompiluj gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.ci przetestuj, /tmp/myadb.loguruchamiając coś LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log. Następnie spróbuj LD_PRELOAD na adb.

meuh
źródło
Cóż, rzeczywiście twoje rozwiązanie jest jedynym, którym udało mi się wykonać pracę jako użytkownik inny niż root. Nie poradziłem sobie z unshare ( Operation not permitted). Mam nadzieję, że opento wystarczy, ale w końcu dodanie unlinktego modułu obsługi nie jest trudne.
gluk47,
Aww. Szkoda, że ​​nie mogę sprawdzić dwóch odpowiedzi. Obiecałem Mikeservowi, że sprawdzi jego rozwiązanie jako odpowiedź, i to rzeczywiście jest realne.
gluk47,
2
nieważne. Dowiedziałem się unsharerównież, więc wszyscy zyskujemy!
Meuh
Po pewnym czasie dziękuję jeszcze raz za próbkę LD_PRELOAD. Odkąd wypróbowałem twój kod, używam LD_PRELOAD w różnych sytuacjach, w których nawet o tym nie pomyślałem. Moje życie zmieniło się na lepsze :)
gluk47
2
@ gluk47 To jest takie cudowne w Gnu / Linux: nigdy nie musisz przestać odkrywać! Jest tyle dobrych rzeczy do odkrycia i udostępnienia.
Meuh