Ist es möglich, einen bestimmten Pfad für einen Prozess zu fälschen?

9

Ich versuche, ADB auf einem Linux-Server mit mehreren Benutzern auszuführen, auf denen ich nicht root bin (um mit meinem Android-Emulator zu spielen). Der adb-Daemon schreibt seine Protokolle in die Datei, /tmp/adb.logdie leider fest in ADB codiert zu sein scheint, und diese Situation wird sich nicht ändern .

Daher kann adb nicht ausgeführt werden, was den offensichtlichen Fehler ergibt : cannot open '/tmp/adb.log': Permission denied. Diese Datei wurde von einem anderen Benutzer erstellt und /tmphat ein Sticky Bit. Wenn ich adb mit dem adb nodaemon serverSchreiben in stdout beginne , treten keine Fehler auf (ich habe auch den Port auf einen eindeutigen Wert eingerichtet, um Konflikte zu vermeiden).

Meine Frage ist: Gibt es eine Möglichkeit, ADB dazu zu bringen, in eine andere Datei zu schreiben als /tmp/adb.log? Gibt es allgemein eine Möglichkeit, eine Art prozessspezifischen Symlink zu erstellen? Ich möchte alle Dateizugriffe auf /tmp/adb.logeine Datei umleiten ~/tmp/adb.log.

Auch hier bin ich auf dem Server nicht als root, so chroot, mount -o rbindund chmodsind nicht gültig Optionen. Wenn möglich, möchte ich ADB-Quellen nicht ändern, aber wenn es keine anderen Lösungen gibt, werde ich das tun.

PS Für den spezifischen ADB-Fall kann ich auf die Ausführung adb nodaemon servermit nohupund die Ausgabeumleitung zurückgreifen , aber die allgemeine Frage ist immer noch relevant.

gluk47
quelle
2
Ja. Sie können Ihren Prozess in einen privaten Mount-Namespace stellen und eine andere Datei überhängen /tmp/adb.logoder sogar eine eigene private Datei /tmpbereitstellen. tun man unshareund man namespacesund man nsenter.
Mikesserv
1
@mikeserv toll, das scheint genau das zu sein was ich brauche, danke! Wenn Sie Ihren Kommentar als Antwort neu formatieren, kann ich ihn als akzeptiert festlegen.
Gluk47
Oder es gibt LD_PRELOADTricks, obwohl das komplizierter wäre.
Thrig
@thrig ja, ich dachte über LD_PRELOAD nach, aber ehrlich gesagt wäre es einfacher /home/$USER/tmp/adb.log, adb fest zu
codieren

Antworten:

5

Hier ist ein sehr einfaches Beispiel für die Verwendung von util-linux's unshare, um einen Prozess in einen privaten Mount-Namespace zu stellen und ihm eine andere Ansicht desselben Dateisystems zu geben, über das sein übergeordnetes Element derzeit verfügt:

{   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

Sie können einem Prozess mit dem unshareDienstprogramm auf aktuellen Linux-Systemen eine private Ansicht seines Dateisystems geben , obwohl die Mount-Namespace-Funktion selbst für die gesamte 3.x-Kernel-Serie ziemlich ausgereift ist. Sie können bereits vorhandene Namespaces aller Art mit dem nsenterDienstprogramm aus demselben Paket eingeben und mit erfahren Sie mehr darüber man.

mikeserv
quelle
Nur eine Frage: Bin ich es oder ist es eine perfekte Lösung, aber nur für den Root-Benutzer?
Gluk47
@ gluk47 - das muss nicht sein. Sie können unsharealle Arten von Namespaces verwenden, um den Benutzernamensraum einzuschließen. Auf diese Weise kann Ihr Benutzer einen Namespace ausführen, in dem er über Root-Zugriff verfügt, und alles, was ein Root-Benutzer möglicherweise vermasselt, wirkt sich nicht auf den übergeordneten Namespace aus. Mit anderen Worten, ein Mount-Namespace kann in einen Benutzernamensraum eingebettet werden. Sie müssen diese manSeiten wirklich lesen . es wird tief. Genau so dockerund so sytemd-nspawnfunktioniert es.
Mikeserv
Ich habe diese Manpages und Beispiele aus dem Internet gelesen.) Es scheint nur, dass ich sie mehr lesen muss. Ich danke Ihnen nur, dass Sie auf diese Technologie hingewiesen haben. Ich war mir dessen überhaupt nicht bewusst.
Gluk47
@ gluk47 - Akzeptiere keine Antworten um der Loyalität willen. Während das Gefühl geschätzt wird, besiegt diese Art von Zeug den Zweck dieses Ortes. Akzeptiere die Antwort, die du verwendest . Wenn dies nicht der Fall ist, akzeptieren Sie diese Antwort bitte nicht . Nur weil ein Prozess als Root gestartet wird , heißt das übrigens nicht, dass er ein Root-Prozess bleiben muss. Es gibt das runuserDienstprogramm, mit dem Sie arbeiten können unshare, und wenn Sie trotzdem bereit sind, kompilierte Programme zu schreiben, gibt es keinen Grund, warum Sie den unshare()Syscall nicht verwenden könnten , um dasselbe zu tun, oder sogar nur system()mit suid binary.
Mikeserv
Ich würde die Antwort sicher nicht akzeptieren, wenn sie nicht nützlich wäre. Ich finde beide Antworten relevant und hilfreich, daher ist das Gefühl der einzige entscheidende Grund, eine dieser Antworten zu überprüfen :)
gluk47
11

LD_PRELOAD ist nicht allzu schwierig und Sie müssen nicht root sein. Fügen Sie Ihre eigene C-Routine ein, die anstelle des Real open()in der C-Bibliothek aufgerufen wird . Ihre Routine prüft, ob die zu öffnende Datei "/tmp/adb.log" ist, und ruft das echte Öffnen mit einem anderen Dateinamen auf. Hier ist deine 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);
}

Kompilieren Sie es mit gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.cund testen Sie es, indem Sie etwas einfügen /tmp/myadb.logund ausführen LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log. Versuchen Sie dann LD_PRELOAD auf adb.

meuh
quelle
Nun, in der Tat ist Ihre Lösung die einzige, die ich als Nicht-Root-Benutzer zum Laufen gebracht habe. Ich habe es nicht geschafft, unshare ( Operation not permitted). Ich hoffe, opendas reicht aus, aber schließlich unlinkist es nicht schwierig, diesen Handler zu erweitern.
Gluk47
Aww. Was für eine Schande, dass ich zwei Antworten nicht überprüfen kann. Ich habe mikeserv versprochen, seine Lösung als Antwort zu prüfen, und es ist in der Tat eine praktikable.
Gluk47
2
keine Ursache. Ich habe es auch gelernt unshare, also gewinnen wir alle!
Meuh
Nach einiger Zeit nochmals vielen Dank für das LD_PRELOAD-Beispiel. Seit ich Ihren Code ausprobiert habe, verwende ich LD_PRELOAD in verschiedenen Situationen, in denen ich nicht einmal daran denken würde. Mein Leben hat sich zum Besseren verändert :)
gluk47
2
@ gluk47 Das ist das Wunderbare an Gnu / Linux: Du musst nie aufhören zu erkunden! Es gibt so viel Gutes zu entdecken und zu teilen.
meuh